@medalsocial/meda 3.6.0 → 3.6.1

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.
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Check } from 'lucide-react';
4
4
  import { cn } from '../../lib/utils.js';
5
+ import { labelText } from '../labels.js';
5
6
  import { LiveStatus } from '../live-status.js';
6
7
  import { slotClass } from '../slots.js';
7
8
  import { renderLabel } from './label-parts.js';
@@ -38,13 +39,13 @@ export function PersonTabs({ people, active, done, onSelect, labels, labelledBy,
38
39
  return (_jsx("div", { role: "tablist", "aria-labelledby": labelledBy, "aria-describedby": describedBy, onKeyDown: onKeyDown, className: "-mx-1 flex gap-2 overflow-x-auto px-1 py-1", children: people.map((person, index) => {
39
40
  const selected = index === active;
40
41
  const finished = done(index);
41
- return (_jsxs("button", { type: "button", role: "tab", id: serviceTabId(index), "aria-selected": selected, "aria-controls": selected ? serviceTabPanelId(index) : undefined, tabIndex: selected ? 0 : -1, onClick: () => onSelect(index),
42
+ return (_jsxs("button", { type: "button", role: "tab", id: serviceTabId(index), "aria-selected": selected, "aria-label": finished ? `${person.label}, ${labelText(labels['service.tabDone'])}` : undefined, "aria-controls": selected ? serviceTabPanelId(index) : undefined, tabIndex: selected ? 0 : -1, onClick: () => onSelect(index),
42
43
  // Weight and ring carry the open tab alongside the colour, as the
43
44
  // category pills do. Focus is an outline, so it stacks on the
44
45
  // selection ring instead of replacing it with a fainter one.
45
46
  className: cn('inline-flex h-11 shrink-0 items-center gap-1.5 rounded-full border-[1.5px] px-4 text-sm whitespace-nowrap transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring', selected
46
47
  ? 'border-primary bg-primary/10 font-bold text-foreground ring-2 ring-primary'
47
- : 'border-primary/35 font-normal text-primary hover:border-primary hover:bg-primary/5', classNames?.tab, selected && classNames?.tabSelected), children: [finished && _jsx(Check, { "aria-hidden": "true", className: "size-4 shrink-0" }), person.label, finished && (_jsxs("span", { className: "sr-only", children: [', ', renderLabel(labels['service.tabDone'])] }))] }, person.key));
48
+ : 'border-primary/35 font-normal text-primary hover:border-primary hover:bg-primary/5', classNames?.tab, selected && classNames?.tabSelected), children: [finished && _jsx(Check, { "aria-hidden": "true", className: "size-4 shrink-0" }), person.label] }, person.key));
48
49
  }) }));
49
50
  }
50
51
  /**
@@ -51,6 +51,12 @@ export interface ServiceScreenCategory {
51
51
  key: string;
52
52
  /** The pill's and the section heading's text. */
53
53
  label: string;
54
+ /**
55
+ * Who the group is for. In a family, a child's menu is `childCategory` plus
56
+ * every group marked `'any'` (an ear piercing, say) — never a grown-ups'
57
+ * group. Absent: the group is a grown-ups' one, as before.
58
+ */
59
+ audience?: 'child' | 'adult' | 'any';
54
60
  }
55
61
  /** A person's «same as last time», and the one-line note when it was swapped (e.g. for age). */
56
62
  export interface ServiceSuggestion {
@@ -193,12 +193,19 @@ function defaultCategoryOf(categories) {
193
193
  * What a person in a party can have is what the business takes online and —
194
194
  * like the booking engine — nothing whose `maxPerBooking` is below the party's
195
195
  * size: the strictest service in a request sets the limit for all of it. With
196
- * a `childCategory`, a child gets that menu and an adult everything else,
197
- * which is often nothing, and is told so.
196
+ * a `childCategory`, a child gets that menu plus the groups meant for anyone
197
+ * (`audience: 'any'`), and an adult everything but the children's — which is
198
+ * often nothing, and they are told so.
198
199
  */
199
200
  function PartyServiceScreen({ services, categories, categoryOf, childCategory, serviceFits = allFit, party, selection, shared, }) {
200
201
  const { Card, labels, classNames } = shared;
201
202
  const fileUnder = categoryOf ?? defaultCategoryOf(categories);
203
+ const keysFor = (audience) => new Set(categories.filter((group) => group.audience === audience).map((group) => group.key));
204
+ // Children's groups: the configured one plus any group marked 'child'.
205
+ const forChildren = keysFor('child');
206
+ if (childCategory !== undefined)
207
+ forChildren.add(childCategory);
208
+ const forAnyone = keysFor('any');
202
209
  const size = party.people.length;
203
210
  const [active, setActive] = useState(0);
204
211
  // Multi-select: the engine books a grown-up's visit alongside the
@@ -212,7 +219,17 @@ function PartyServiceScreen({ services, categories, categoryOf, childCategory, s
212
219
  // note below that books the wrong thing.
213
220
  childCategory === undefined
214
221
  ? fits
215
- : fits.filter((service) => (fileUnder(service) === childCategory) === !person.adult), (service) => serviceFits(service, index));
222
+ : fits.filter((service) => {
223
+ const key = fileUnder(service);
224
+ if (forChildren.has(key))
225
+ return !person.adult;
226
+ // A child gets an «anyone» group only through an EXPLICIT mapping:
227
+ // the caller's own `categoryOf` when it passes one (its mapping is
228
+ // its decision), else the service's own category — never the
229
+ // built-in display fallback, which files an unlisted (possibly
230
+ // grown-up) category under the last group.
231
+ return person.adult || forAnyone.has(categoryOf ? key : service.category);
232
+ }), (service) => serviceFits(service, index));
216
233
  const ticked = selection ? tickedBy(selection, index) : null;
217
234
  const chosen = party.choices[index]?.id ?? null;
218
235
  const suggestion = person.suggestion ?? null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medalsocial/meda",
3
- "version": "3.6.0",
3
+ "version": "3.6.1",
4
4
  "description": "Shared Meda UI shell and runtime package.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {