@medalsocial/meda 3.9.1 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -131,6 +131,13 @@ export interface WhoScreenProps {
|
|
|
131
131
|
adult: WizardPerson & {
|
|
132
132
|
adult: true;
|
|
133
133
|
};
|
|
134
|
+
/**
|
|
135
|
+
* Start with nobody, rather than one child: for a salon where a grown-up
|
|
136
|
+
* booking for themselves is as common as a child. Children go down to 0
|
|
137
|
+
* and the grown-up's row is a free choice either way; «next» waits for an
|
|
138
|
+
* answer. Default `false`.
|
|
139
|
+
*/
|
|
140
|
+
startEmpty?: boolean;
|
|
134
141
|
};
|
|
135
142
|
/** Shown under the heading for a guest — e.g. an offer to log in. */
|
|
136
143
|
loginRow?: ReactNode;
|
|
@@ -142,7 +142,8 @@ export function WhoScreen({ labels, format, people, family, guestChoices, addedC
|
|
|
142
142
|
* A guest's party: how many children (a stepper) and whether the grown-up
|
|
143
143
|
* comes too (a check row), so «a child and me» is one booking without an
|
|
144
144
|
* account. Starts at one child, which is the common answer, so «next» is live
|
|
145
|
-
* from the first frame
|
|
145
|
+
* from the first frame — or, with `startEmpty`, at nobody, so a grown-up
|
|
146
|
+
* booking for themselves is one tap like anyone else.
|
|
146
147
|
*/
|
|
147
148
|
function GuestParty({ party, people, maxPeople, labels, classNames, onChoose, }) {
|
|
148
149
|
const adultOn = people.some((person) => person.key === party.adult.key);
|
|
@@ -154,8 +155,10 @@ function GuestParty({ party, people, maxPeople, labels, classNames, onChoose, })
|
|
|
154
155
|
// stepper adds and removes the unnamed ones after them.
|
|
155
156
|
const generatedKeys = new Set(Array.from({ length: maxPeople }, (_, index) => party.child(index + 1).key));
|
|
156
157
|
const named = childSeats.filter((person) => !generatedKeys.has(person.key));
|
|
158
|
+
const startEmpty = party.startEmpty === true;
|
|
157
159
|
// The stepper never takes a named child away: their own card does that.
|
|
158
|
-
|
|
160
|
+
// Without `startEmpty` it never empties the booking either.
|
|
161
|
+
const floor = Math.max(named.length, adultOn || startEmpty ? 0 : 1);
|
|
159
162
|
const answer = (count, withAdult) => {
|
|
160
163
|
const kept = named;
|
|
161
164
|
const used = new Set(kept.map((person) => person.key));
|
|
@@ -174,9 +177,11 @@ function GuestParty({ party, people, maxPeople, labels, classNames, onChoose, })
|
|
|
174
177
|
const wasEmpty = useRef(false);
|
|
175
178
|
useEffect(() => {
|
|
176
179
|
const empty = people.length === 0;
|
|
177
|
-
if (empty && !wasEmpty.current)
|
|
180
|
+
if (empty && !wasEmpty.current && !startEmpty)
|
|
178
181
|
answer(1, false);
|
|
179
|
-
|
|
182
|
+
// An empty answer `startEmpty` left alone is not one already seen: if the
|
|
183
|
+
// flag goes, the child is still filled in.
|
|
184
|
+
wasEmpty.current = empty && !startEmpty;
|
|
180
185
|
});
|
|
181
186
|
const text = (key, values = {}) => {
|
|
182
187
|
const label = labels[key];
|
|
@@ -184,8 +189,9 @@ function GuestParty({ party, people, maxPeople, labels, classNames, onChoose, })
|
|
|
184
189
|
};
|
|
185
190
|
const stepButton = 'flex size-11 shrink-0 items-center justify-center rounded-full border border-border bg-background text-foreground transition-colors hover:border-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary disabled:pointer-events-none disabled:opacity-40';
|
|
186
191
|
return (_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: cn('flex items-center gap-4 rounded-lg border border-border bg-card px-5 py-4', classNames?.card), children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "font-semibold", children: text('who.party.children') }), labels['who.party.childrenNote'] && (_jsx("p", { className: "text-sm text-muted-foreground", children: renderLabel(labels['who.party.childrenNote']) }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { type: "button", "aria-label": text('who.party.fewer'), disabled: children <= floor, onClick: () => answer(children - 1, adultOn), className: stepButton, children: _jsx(Minus, { "aria-hidden": "true", className: "size-4" }) }), _jsxs("span", { "aria-live": "polite", "aria-atomic": "true", className: "w-6 text-center text-lg font-bold tabular-nums", children: [_jsx("span", { "aria-hidden": "true", children: children }), _jsx("span", { className: "sr-only", children: text('who.party.count', { count: children }) })] }), _jsx("button", { type: "button", "aria-label": text('who.party.more'), "aria-describedby": full ? LIMIT_ID : undefined, disabled: full, onClick: () => answer(children + 1, adultOn), className: stepButton, children: _jsx(Plus, { "aria-hidden": "true", className: "size-4" }) })] })] }), _jsxs(CheckRow, { checked: adultOn,
|
|
187
|
-
// Never down to nobody: the last person in the booking stays
|
|
188
|
-
|
|
192
|
+
// Never down to nobody: the last person in the booking stays — unless
|
|
193
|
+
// the booking may start (and so stand) empty.
|
|
194
|
+
disabled: (!adultOn && full) || (adultOn && children === 0 && !startEmpty), describedBy: !adultOn && full ? LIMIT_ID : undefined, onToggle: () => answer(children, !adultOn), className: cn('min-h-14 justify-between px-5 py-3 text-sm', classNames?.card), selectedClassName: classNames?.cardSelected, children: [_jsxs("span", { className: "flex min-w-0 flex-col text-left", children: [_jsx("span", { className: "font-medium", children: text('who.party.adult') }), labels['who.party.adultNote'] && children > 0 && (_jsx("span", { className: "text-muted-foreground", children: renderLabel(labels['who.party.adultNote']) }))] }), _jsx(CheckCircle, { checked: adultOn })] }), _jsx("p", { id: LIMIT_ID, "aria-live": "polite", className: slotClass(classNames, 'limit', 'min-h-5 text-sm text-muted-foreground'), children: full ? renderLabel(labels['who.family.limit'], { max: maxPeople }) : '' })] }));
|
|
189
195
|
}
|
|
190
196
|
/**
|
|
191
197
|
* The guest chips as a radio group. Roving focus: one chip is in the tab
|