Parsware
All articles

Parsware Platform

Modals and drawers

Two overlays on the Contact screen: a modal that asks before a contact is deleted and can't be walked away from, and a drawer that slides in from the end of the line to change the job title. Each is a drawn card, ticked as an overlay card and opened by one line in a handler.

A modal over the Contact screen asking "Delete this contact?" with Keep it and Delete buttons, next to the title "Modals and drawers"

Some things shouldn't need a screen of their own. Are you sure? is one. A quick change to one field is another. For those, a Studio screen has overlays: a modal in the middle of the screen, a drawer that slides in from one side, and a dropdown that hangs off a button.

This article adds two to the Contact screen from the last article: a modal that asks before deleting, and a drawer for editing the job title.

What an overlay is

An overlay isn't a screen. It's a card: a frame you draw anywhere on the canvas, make into a component, and tick as An overlay card. It isn't shown until a handler opens it, and the handler decides where:

Handler Opens the card Closes it
showModal("Name") in the middle, over a shaded screen closeModal()
openStartDrawer("Name") as a panel at the start of the line closeStartDrawer()
openEndDrawer("Name") as a panel at the end of the line closeEndDrawer()
openDropdown("Name") hanging off whatever ran the handler closeDropdown()

Where you draw the card on the canvas doesn't matter. Its width does: the drawn width is the modal's or the drawer's width.

1. Draw the question

Away from both screens, draw a frame 400 by 184 and name it Delete contact. Put in it a heading, Delete this contact?, a paragraph saying what will happen, and two Buttons, Keep it and Delete.

Keep it closes the card: closeModal();. Delete does the work:

deleteRecord("contact", getState("record").id);
notify("Deleted " + getState("record").fullname + ".");
closeModal();
navigate("Contacts");

The code page for the Delete button: Pressed marked Written, with deleteRecord, notify, closeModal and navigate in the editor

The card's buttons read the screen's state, so getState("record") is the contact that's open. The lines run in order and each waits for the one before, so the list that navigate opens has already lost the contact.

2. Make it an overlay card

Right-click the card's frame on the canvas and choose Make component. In Properties, tick An overlay card. Then tick It has to be answered as well.

The Delete contact card on the canvas, and its Component panel with An overlay card and It has to be answered both ticked

A card that has to be answered can't be closed by pressing Escape or clicking beside it, which is why it needs its own Keep it. Use it only for questions that walking away can't answer. Delete this contact? is one: clicking beside it isn't a yes or a no.

Fill the card before you make it a component. Afterwards it's a component's main frame, and what you place over it lands beside it on the canvas rather than in it.

3. Draw the drawer

Draw a second frame, 360 by 220, named Edit job title. Put a heading in it, a Text box whose Value is bound to state.record.jobtitle, and a Save button:

updateRecord("contact", getState("record").id, { jobtitle: getState("record").jobtitle });
notify("Saved.");
closeEndDrawer();

The code page for the drawer's Save button: Pressed marked Written, with updateRecord, notify and closeEndDrawer in the editor

The text box writes what's typed straight into state.record.jobtitle, so the heading and the line under it on the screen change as you type. Make this frame a component and tick An overlay card too, but leave It has to be answered off: a drawer opened by mistake should close the way it opened.

4. Open them

On the Contact screen, place two more buttons beside Back to contacts:

  • Edit job title: openEndDrawer("Edit job title");
  • Delete…: showModal("Delete contact");

The code page for the Delete… button: Pressed marked Written, with showModal("Delete contact") in the editor

The name in quotes is the component's name. The three dots in Delete… tell people a question comes before anything happens.

5. Use them

Save, open a contact in the Shell, and press Edit job title. The drawer slides in from the right, and the line under the name follows what you type:

The drawer open on the right with Job title and Head of buying in the box, and the Contact screen shaded behind it, its line reading aaliyah.brooks@example.com · Head of buying

Press Save, and the change is stored. Now press Delete…:

The modal in the middle of the shaded Contact screen: Delete this contact?, the warning, and Keep it and Delete

Escape does nothing, and neither does clicking the shaded screen. Keep it closes it. Delete deletes, and you're back on the list with a message:

The Contacts list with "Deleted Aaliyah Brooks." at the top and 100 contacts · page 1

Start and end, not left and right

Switch the Shell to Persian or Arabic and press Edit job title again. The drawer arrives from the left:

The same Contact screen in Persian, laid out right to left, with the Job title drawer open on the left

That's what end means: the end of the line you're reading. In English that's the right, in Persian it's the left. Nothing in the screen says left or right, so one drawing is correct in both.

What we fixed

Building these turned up a bug, now fixed:

  • A closed drawer reported errors on another screen. Every card in an app is part of every one of its screens, and the screen worked out what the card would show even while it was closed. Our drawer reads state.record, which only the Contact screen has, so back on the list the Shell showed A binding … could not run: Object member 'record' not found, nine times, about a drawer nobody had opened. A card is now worked out only while it's open.

Try it

Import the Overlays sample (studio-overlays). It has all four: a dropdown menu under an avatar, a modal that asks before deleting, and a drawer from each side. Open it in Persian and watch the two drawers swap sides.

Next: links to screens, records and outside pages.