@medalsocial/meda 3.12.0 → 3.14.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.
@@ -31,7 +31,7 @@ import { WHO_SCREEN_LABEL_KEYS, WHO_SCREEN_PARTY_LABEL_KEYS } from './who-screen
31
31
  * (Screens that embed another screen already include its keys, e.g. the
32
32
  * login sheet carries the login panel's, OTP slots' and Vipps button's.)
33
33
  */
34
- export declare const BOOKING_LABEL_KEYS: readonly ("addChild.trigger" | "addChild.title" | "addChild.description.saved" | "addChild.description.local" | "addChild.name" | "addChild.birthYear" | "addChild.birthYearPlaceholder" | "addChild.birthMonth" | "addChild.birthMonthUnknown" | "addChild.notes" | "addChild.missing" | "addChild.submit" | "addChild.close" | "panel.heading" | "panel.who" | "panel.service" | "panel.time" | "panel.stylist" | "panel.notChosen" | "panel.firstAvailable" | "panel.alsoFree" | "panel.swap" | "panel.swapTo" | "panel.total" | "panel.continue" | "recap.label" | "recap.time" | "recap.line" | "recap.lineFor" | "recap.anyStylist" | "recap.total" | "recap.edit" | "recap.editLabel" | "recap.alsoFree" | "recap.swapTo" | "bookingSkeleton.opening" | "confirmation.heading" | "confirmation.serviceFor" | "confirmation.when" | "confirmation.party.heading" | "confirmation.party.line" | "confirmation.party.stylist" | "confirmation.party.total" | "confirmation.calendar" | "confirmation.manage" | "confirmation.manageFor" | "confirmation.portal" | "confirmation.startOver" | "confirmation.sent" | "confirmation.leafTime" | "confirmation.detail.service" | "confirmation.detail.stylist" | "confirmation.detail.where" | "confirmation.duration" | "confirmation.directions" | "confirmation.tip.heading" | "confirmation.tip.body" | "confirmation.manageUntil" | "details.heading" | "details.phone.label" | "details.phone.prefix" | "details.phone.error" | "details.name.label" | "details.known.label" | "details.known.for" | "details.known.self" | "details.known.adult" | "details.known.child" | "details.child.name" | "details.child.year" | "details.child.yearPlaceholder" | "details.email.label" | "details.email.help" | "details.email.lockedHelp" | "details.email.error" | "details.notes.label" | "details.notes.placeholder" | "details.terms.text" | "details.terms.link" | "details.marketing.text" | "details.error.maxParty" | "details.error.slotTaken" | "details.error.conflict" | "details.error.invalidInput" | "details.error.unconfigured" | "details.error.upstreamError" | "details.error.inProgress" | "details.call.link" | "details.call.none" | "details.submit" | "otp.label" | "vipps.button" | "vipps.unavailable" | "vipps.throttled" | "login.heading" | "login.codeHeading" | "login.or" | "login.continueEmail" | "login.emailIntro" | "login.emailLabel" | "login.sendCode" | "login.codeLabel" | "login.submitCode" | "login.codeHelp" | "login.codeHelpVipps" | "login.vippsSentTo" | "login.vippsSentToUnknown" | "login.cooldownWait" | "login.cooldownReady" | "login.resendIn" | "login.resend" | "login.switchEmail" | "login.useEmailInstead" | "login.notice.sent" | "login.notice.resent" | "login.notice.unreachable" | "login.notice.badEmail" | "login.notice.invalid" | "login.notice.throttled" | "login.notice.vippsInvalid" | "login.notice.vippsConflict" | "loginSheet.prompt" | "loginSheet.trigger" | "loginSheet.intro" | "loginSheet.vipps" | "loginSheet.close" | "time.heading" | "time.dayStrip.legend" | "time.nearest" | "time.current" | "time.nextFree" | "time.shortNotice" | "time.weekend.pct" | "time.weekend.mixed" | "time.month.show" | "time.month.hide" | "time.month.previous" | "time.month.next" | "time.month.heading" | "time.month.note" | "time.party.sequential" | "time.party.parallel" | "time.party.seat" | "time.party.seatSeparator" | "time.parallel.offer.two" | "time.parallel.offer.other" | "time.parallel.accept" | "time.empty.closed" | "time.empty.over" | "time.empty.full" | "time.empty.stylist" | "time.empty.unknown" | "time.empty.callLink" | "time.empty.callLinkAria" | "time.empty.callSuffix" | "time.empty.callPlain" | "time.loading" | "time.skeleton.dayPlaceholder" | "time.taken.message" | "time.taken.close" | "time.soonest.heading" | "time.soonest.pick" | "time.soonest.with" | "time.dayChip.free" | "time.dayChip.freeOne" | "time.dayChip.full" | "time.week.none" | "time.week.more" | "manage.heading" | "manage.serviceFallback" | "manage.card.named" | "manage.card.when" | "manage.card.whenWithStylist" | "manage.appointment.named" | "manage.appointment.namedSibilant" | "manage.appointment.unnamed" | "manage.ringUs" | "manage.ringUsOn" | "manage.callUs" | "manage.callUsOn" | "manage.error.windowPassed" | "manage.error.slotTaken" | "manage.error.notFound" | "manage.error.conflict" | "manage.error.invalidInput" | "manage.error.unavailable" | "manage.freeUntil" | "manage.remaining.in" | "manage.remaining.daysAndHours" | "manage.remaining.underMinute" | "manage.remaining.day.one" | "manage.remaining.day.other" | "manage.remaining.hour.one" | "manage.remaining.hour.other" | "manage.remaining.minute.one" | "manage.remaining.minute.other" | "manage.change" | "manage.cancel" | "manage.callToChange" | "manage.callToCancel" | "manage.blocked" | "manage.blocked.suffix" | "manage.blocked.plain" | "manage.calendar" | "manage.closed.cancelled" | "manage.closed.completed" | "manage.closed.noShow" | "manage.closed.pending" | "manage.closed.pendingCall" | "manage.closed.pendingCallPlain" | "manage.bookNew" | "manage.party.heading" | "manage.party.justThisOne" | "manage.party.unnamed" | "manage.party.note" | "manage.party.noteSuffix" | "manage.back" | "manage.reschedule.lead" | "manage.reschedule.leadWithStylist" | "manage.reschedule.failed" | "manage.reschedule.failedSuffix" | "manage.reschedule.loading" | "manage.move.question" | "manage.move.questionWithStylist" | "manage.move.surcharge" | "manage.move.confirm" | "manage.move.pickAnother" | "manage.cancel.question" | "manage.cancel.reasonLegend" | "manage.cancel.reason.illness" | "manage.cancel.reason.noLongerSuits" | "manage.cancel.reason.other" | "manage.cancel.confirm" | "manage.cancel.keep" | "manage.cancelled" | "manage.cancelled.findNew" | "manage.moved" | "manage.movedTo" | "manage.moved.view" | "manage.moved.noLink" | "manage.moved.noLinkSuffix" | "manage.portal" | "childCards.empty" | "childCards.neverVisited" | "childCards.lastVisit" | "childCards.lastVisitNoService" | "childCards.book" | "childCards.stylist" | "childCards.next" | "childCards.none" | "dataControls.heading" | "dataControls.export" | "dataControls.delete" | "dataControls.confirmHeading" | "dataControls.consequences" | "dataControls.confirmLabel" | "dataControls.confirmHelp" | "dataControls.wrongWord" | "dataControls.deleteForever" | "dataControls.cancel" | "dataControls.unreachable" | "dataControls.confirmWord" | "ageConfirm.heading" | "ageConfirm.lead" | "ageConfirm.legend" | "ageConfirm.birthYear" | "ageConfirm.birthMonth" | "ageConfirm.birthMonthUnknown" | "ageConfirm.confirm" | "ageConfirm.dismiss" | "familyEditor.heading" | "familyEditor.lead" | "familyEditor.empty" | "familyEditor.legend" | "familyEditor.add" | "familyEditor.name" | "familyEditor.birthYear" | "familyEditor.birthYearPlaceholder" | "familyEditor.birthMonth" | "familyEditor.birthMonthUnknown" | "familyEditor.stylist" | "familyEditor.stylistNone" | "familyEditor.stylistFormer" | "familyEditor.notes" | "familyEditor.notesPlaceholder" | "familyEditor.newRow" | "familyEditor.save" | "familyEditor.remove" | "familyEditor.removeNamed" | "familyEditor.removeRow" | "familyEditor.saved" | "familyEditor.savedNoDetails" | "familyEditor.partial" | "familyEditor.removed" | "familyEditor.unreachable" | "familyEditor.ageConfirmed" | "familyEditor.ageSaved" | "logout.button" | "logout.unreachable" | "portalShell.nav" | "portalShell.navBar" | "portalUnreachable.heading" | "portalUnreachable.body" | "portalUnreachable.retry" | "portalUnreachable.call" | "profileForm.heading" | "profileForm.firstName" | "profileForm.lastName" | "profileForm.phone" | "profileForm.email" | "profileForm.emailHelp" | "profileForm.save" | "profileForm.saved" | "profileForm.restored" | "profileForm.marketing" | "profileForm.unreachable" | "rebook.heading" | "rebook.empty" | "rebook.serviceFallback" | "rebook.anyStylist" | "rebook.cta" | "upcoming.heading" | "upcoming.empty" | "upcoming.book" | "upcoming.next" | "upcoming.today" | "upcoming.tomorrow" | "upcoming.inDays" | "upcoming.when" | "upcoming.serviceFallback" | "upcoming.anyStylist" | "upcoming.manage" | "upcoming.noManage" | "upcoming.noManageWithPhone" | "upcoming.call" | "vippsLink.heading" | "vippsLink.linked" | "vippsLink.pitch" | "vippsLink.button" | "vippsLink.success" | "vippsLink.conflict" | "vippsLink.failed" | "vippsLink.unavailable" | "vippsLink.throttled" | "history.heading" | "history.empty" | "history.serviceFallback" | "history.count.one" | "history.count.other" | "history.unknownAmount" | "service.multiHint" | "service.total" | "service.chooseFirst" | "service.continue" | "service.party.nothingBookable" | "service.tabDone" | "service.heading" | "service.categoriesLegend" | "service.sameAsLast" | "service.chooseOther" | "service.duration" | "service.phoneOnly" | "service.phoneOnlyLinkLabel" | "service.ageDivider.named" | "service.ageDivider.unnamed" | "service.party.listLabel" | "service.party.adultAlone" | "service.party.nothingFits" | "service.party.remove" | "service.search" | "service.searchLabel" | "service.searchEmpty" | "stylist.heading" | "stylist.firstAvailable" | "stylist.firstAvailableSubtitle" | "stylist.firstAvailableBadge" | "stylist.pendingName" | "stylist.nextAvailable" | "stylist.loading" | "stylist.party.legend" | "stylist.party.sequential" | "stylist.party.sequentialMinutes" | "stylist.party.parallel.two" | "stylist.party.parallel.three" | "stylist.party.parallel.other" | "stylist.party.parallelMinutes" | "stylist.party.parallelNote.two" | "stylist.party.parallelNote.three" | "stylist.party.parallelNote.other" | "stylist.party.seatsHeading" | "stylist.party.seat" | "summary.placeholder.who" | "summary.placeholder.service" | "summary.next" | "summary.hint.who" | "summary.hint.service" | "summary.hint.when" | "who.heading" | "who.guest.groupLabel" | "who.guest.children.one" | "who.guest.children.other" | "who.guest.adult" | "who.guest.addedList" | "who.family.legend" | "who.family.self" | "who.family.limit" | "who.party.children" | "who.party.childrenNote" | "who.party.adult" | "who.party.adultNote" | "who.party.fewer" | "who.party.more" | "who.party.count" | "who.party.adultFewer" | "who.party.adultMore" | "who.party.adultCount" | "who.party.lead" | "who.party.togetherHint")[];
34
+ export declare const BOOKING_LABEL_KEYS: readonly ("addChild.trigger" | "addChild.title" | "addChild.description.saved" | "addChild.description.local" | "addChild.name" | "addChild.birthYear" | "addChild.birthYearPlaceholder" | "addChild.birthMonth" | "addChild.birthMonthUnknown" | "addChild.notes" | "addChild.missing" | "addChild.submit" | "addChild.close" | "panel.heading" | "panel.who" | "panel.service" | "panel.time" | "panel.stylist" | "panel.notChosen" | "panel.firstAvailable" | "panel.alsoFree" | "panel.swap" | "panel.swapTo" | "panel.total" | "panel.continue" | "recap.label" | "recap.time" | "recap.line" | "recap.lineFor" | "recap.anyStylist" | "recap.total" | "recap.edit" | "recap.editLabel" | "recap.alsoFree" | "recap.swapTo" | "bookingSkeleton.opening" | "confirmation.heading" | "confirmation.serviceFor" | "confirmation.when" | "confirmation.party.heading" | "confirmation.party.line" | "confirmation.party.stylist" | "confirmation.party.total" | "confirmation.calendar" | "confirmation.manage" | "confirmation.manageFor" | "confirmation.portal" | "confirmation.startOver" | "confirmation.sent" | "confirmation.leafTime" | "confirmation.detail.service" | "confirmation.detail.stylist" | "confirmation.detail.where" | "confirmation.duration" | "confirmation.directions" | "confirmation.tip.heading" | "confirmation.tip.body" | "confirmation.manageUntil" | "details.heading" | "details.phone.label" | "details.phone.prefix" | "details.phone.error" | "details.name.label" | "details.known.label" | "details.known.for" | "details.known.self" | "details.known.adult" | "details.known.child" | "details.child.name" | "details.child.year" | "details.child.yearPlaceholder" | "details.email.label" | "details.email.help" | "details.email.lockedHelp" | "details.email.error" | "details.notes.label" | "details.notes.placeholder" | "details.terms.text" | "details.terms.link" | "details.marketing.text" | "details.error.maxParty" | "details.error.slotTaken" | "details.error.conflict" | "details.error.invalidInput" | "details.error.unconfigured" | "details.error.upstreamError" | "details.error.inProgress" | "details.call.link" | "details.call.none" | "details.submit" | "otp.label" | "vipps.button" | "vipps.unavailable" | "vipps.throttled" | "login.heading" | "login.codeHeading" | "login.or" | "login.continueEmail" | "login.emailIntro" | "login.emailLabel" | "login.sendCode" | "login.codeLabel" | "login.submitCode" | "login.codeHelp" | "login.codeHelpVipps" | "login.vippsSentTo" | "login.vippsSentToUnknown" | "login.cooldownWait" | "login.cooldownReady" | "login.resendIn" | "login.resend" | "login.switchEmail" | "login.useEmailInstead" | "login.notice.sent" | "login.notice.resent" | "login.notice.unreachable" | "login.notice.badEmail" | "login.notice.invalid" | "login.notice.throttled" | "login.notice.vippsInvalid" | "login.notice.vippsConflict" | "loginSheet.prompt" | "loginSheet.trigger" | "loginSheet.intro" | "loginSheet.vipps" | "loginSheet.close" | "time.heading" | "time.dayStrip.legend" | "time.nearest" | "time.current" | "time.nextFree" | "time.shortNotice" | "time.weekend.pct" | "time.weekend.mixed" | "time.month.show" | "time.month.hide" | "time.month.previous" | "time.month.next" | "time.month.heading" | "time.month.note" | "time.party.sequential" | "time.party.parallel" | "time.party.seat" | "time.party.seatSeparator" | "time.parallel.offer.two" | "time.parallel.offer.other" | "time.parallel.accept" | "time.empty.closed" | "time.empty.over" | "time.empty.full" | "time.empty.stylist" | "time.empty.unknown" | "time.empty.callLink" | "time.empty.callLinkAria" | "time.empty.callSuffix" | "time.empty.callPlain" | "time.loading" | "time.skeleton.dayPlaceholder" | "time.taken.message" | "time.taken.close" | "time.soonest.heading" | "time.soonest.pick" | "time.soonest.with" | "time.dayChip.free" | "time.dayChip.freeOne" | "time.dayChip.full" | "time.week.none" | "time.week.more" | "manage.heading" | "manage.serviceFallback" | "manage.card.named" | "manage.card.when" | "manage.card.whenWithStylist" | "manage.appointment.named" | "manage.appointment.namedSibilant" | "manage.appointment.unnamed" | "manage.ringUs" | "manage.ringUsOn" | "manage.callUs" | "manage.callUsOn" | "manage.error.windowPassed" | "manage.error.slotTaken" | "manage.error.notFound" | "manage.error.conflict" | "manage.error.invalidInput" | "manage.error.unavailable" | "manage.freeUntil" | "manage.remaining.in" | "manage.remaining.daysAndHours" | "manage.remaining.underMinute" | "manage.remaining.day.one" | "manage.remaining.day.other" | "manage.remaining.hour.one" | "manage.remaining.hour.other" | "manage.remaining.minute.one" | "manage.remaining.minute.other" | "manage.change" | "manage.cancel" | "manage.callToChange" | "manage.callToCancel" | "manage.blocked" | "manage.blocked.suffix" | "manage.blocked.plain" | "manage.calendar" | "manage.closed.cancelled" | "manage.closed.completed" | "manage.closed.noShow" | "manage.closed.pending" | "manage.closed.pendingCall" | "manage.closed.pendingCallPlain" | "manage.bookNew" | "manage.party.heading" | "manage.party.justThisOne" | "manage.party.unnamed" | "manage.party.note" | "manage.party.noteSuffix" | "manage.back" | "manage.reschedule.lead" | "manage.reschedule.leadWithStylist" | "manage.reschedule.failed" | "manage.reschedule.failedSuffix" | "manage.reschedule.loading" | "manage.move.question" | "manage.move.questionWithStylist" | "manage.move.surcharge" | "manage.move.confirm" | "manage.move.pickAnother" | "manage.cancel.question" | "manage.cancel.reasonLegend" | "manage.cancel.reason.illness" | "manage.cancel.reason.noLongerSuits" | "manage.cancel.reason.other" | "manage.cancel.confirm" | "manage.cancel.keep" | "manage.cancelled" | "manage.cancelled.findNew" | "manage.moved" | "manage.movedTo" | "manage.moved.view" | "manage.moved.noLink" | "manage.moved.noLinkSuffix" | "manage.portal" | "childCards.empty" | "childCards.neverVisited" | "childCards.lastVisit" | "childCards.lastVisitNoService" | "childCards.book" | "childCards.stylist" | "childCards.next" | "childCards.none" | "dataControls.heading" | "dataControls.export" | "dataControls.delete" | "dataControls.confirmHeading" | "dataControls.consequences" | "dataControls.confirmLabel" | "dataControls.confirmHelp" | "dataControls.wrongWord" | "dataControls.deleteForever" | "dataControls.cancel" | "dataControls.unreachable" | "dataControls.confirmWord" | "ageConfirm.heading" | "ageConfirm.lead" | "ageConfirm.legend" | "ageConfirm.birthYear" | "ageConfirm.birthMonth" | "ageConfirm.birthMonthUnknown" | "ageConfirm.confirm" | "ageConfirm.dismiss" | "familyEditor.heading" | "familyEditor.lead" | "familyEditor.empty" | "familyEditor.legend" | "familyEditor.add" | "familyEditor.name" | "familyEditor.birthYear" | "familyEditor.birthYearPlaceholder" | "familyEditor.birthMonth" | "familyEditor.birthMonthUnknown" | "familyEditor.stylist" | "familyEditor.stylistNone" | "familyEditor.stylistFormer" | "familyEditor.notes" | "familyEditor.notesPlaceholder" | "familyEditor.newRow" | "familyEditor.save" | "familyEditor.remove" | "familyEditor.removeNamed" | "familyEditor.removeRow" | "familyEditor.saved" | "familyEditor.savedNoDetails" | "familyEditor.partial" | "familyEditor.removed" | "familyEditor.unreachable" | "familyEditor.ageConfirmed" | "familyEditor.ageSaved" | "logout.button" | "logout.unreachable" | "portalShell.nav" | "portalShell.navBar" | "portalUnreachable.heading" | "portalUnreachable.body" | "portalUnreachable.retry" | "portalUnreachable.call" | "profileForm.heading" | "profileForm.firstName" | "profileForm.lastName" | "profileForm.phone" | "profileForm.email" | "profileForm.emailHelp" | "profileForm.save" | "profileForm.saved" | "profileForm.restored" | "profileForm.marketing" | "profileForm.unreachable" | "rebook.heading" | "rebook.empty" | "rebook.serviceFallback" | "rebook.anyStylist" | "rebook.cta" | "upcoming.heading" | "upcoming.empty" | "upcoming.book" | "upcoming.next" | "upcoming.today" | "upcoming.tomorrow" | "upcoming.inDays" | "upcoming.when" | "upcoming.serviceFallback" | "upcoming.anyStylist" | "upcoming.manage" | "upcoming.noManage" | "upcoming.noManageWithPhone" | "upcoming.call" | "vippsLink.heading" | "vippsLink.linked" | "vippsLink.pitch" | "vippsLink.button" | "vippsLink.success" | "vippsLink.conflict" | "vippsLink.failed" | "vippsLink.unavailable" | "vippsLink.throttled" | "history.heading" | "history.empty" | "history.serviceFallback" | "history.count.one" | "history.count.other" | "history.unknownAmount" | "service.multiHint" | "service.total" | "service.chooseFirst" | "service.continue" | "service.party.nothingBookable" | "service.tabDone" | "service.heading" | "service.categoriesLegend" | "service.sameAsLast" | "service.chooseOther" | "service.duration" | "service.phoneOnly" | "service.phoneOnlyLinkLabel" | "service.ageDivider.named" | "service.ageDivider.unnamed" | "service.party.listLabel" | "service.party.adultAlone" | "service.party.nothingFits" | "service.party.remove" | "service.search" | "service.searchLabel" | "service.searchEmpty" | "stylist.heading" | "stylist.firstAvailable" | "stylist.firstAvailableSubtitle" | "stylist.firstAvailableBadge" | "stylist.pendingName" | "stylist.nextAvailable" | "stylist.loading" | "stylist.party.legend" | "stylist.party.sequential" | "stylist.party.sequentialMinutes" | "stylist.party.parallel.two" | "stylist.party.parallel.three" | "stylist.party.parallel.other" | "stylist.party.parallelMinutes" | "stylist.party.parallelNote.two" | "stylist.party.parallelNote.three" | "stylist.party.parallelNote.other" | "stylist.party.seatsHeading" | "stylist.party.seat" | "summary.placeholder.who" | "summary.placeholder.service" | "summary.next" | "summary.hint.who" | "summary.hint.service" | "summary.hint.when" | "who.heading" | "who.guest.groupLabel" | "who.guest.children.one" | "who.guest.children.other" | "who.guest.adult" | "who.guest.addedList" | "who.family.legend" | "who.family.self" | "who.family.limit" | "who.party.children" | "who.party.childrenNote" | "who.party.adult" | "who.party.adultNote" | "who.party.fewer" | "who.party.more" | "who.party.count" | "who.party.adultFewer" | "who.party.adultMore" | "who.party.adultCount" | "who.party.lead" | "who.party.togetherHint" | "who.family.adults")[];
35
35
  export type BookingLabelKey = BookingRequiredLabelKey | BookingOptionalLabelKey;
36
36
  /**
37
37
  * Keys added after a screen first shipped, for a feature a caller opts into.
@@ -41,7 +41,7 @@ export declare const WHO_SCREEN_LABEL_KEYS: readonly ["who.heading", "who.guest.
41
41
  * stepper buttons' accessible names; `who.party.count` `{count}` is read out
42
42
  * when the number changes.
43
43
  */
44
- export declare const WHO_SCREEN_PARTY_LABEL_KEYS: readonly ["who.party.children", "who.party.childrenNote", "who.party.adult", "who.party.adultNote", "who.party.fewer", "who.party.more", "who.party.count", "who.party.adultFewer", "who.party.adultMore", "who.party.adultCount", "who.party.lead", "who.party.togetherHint"];
44
+ export declare const WHO_SCREEN_PARTY_LABEL_KEYS: readonly ["who.party.children", "who.party.childrenNote", "who.party.adult", "who.party.adultNote", "who.party.fewer", "who.party.more", "who.party.count", "who.party.adultFewer", "who.party.adultMore", "who.party.adultCount", "who.party.lead", "who.party.togetherHint", "who.family.adults"];
45
45
  export type WhoScreenLabels = Record<(typeof WHO_SCREEN_LABEL_KEYS)[number], BookingLabel> & Partial<Record<(typeof WHO_SCREEN_PARTY_LABEL_KEYS)[number], BookingLabel>> & AddChildSheetLabels;
46
46
  /**
47
47
  * - `root` — the `<section>`
@@ -143,6 +143,11 @@ export interface WhoScreenProps {
143
143
  * rather than one check row: two parents, or a parent and a grandparent,
144
144
  * in one booking. Seat 1 must be `adult`. Needs the `who.party.adultFewer`,
145
145
  * `adultMore` and `adultCount` labels; without them the check row stays.
146
+ *
147
+ * The one part of `guestParty` a logged-in parent's list reads too: with
148
+ * `who.family.adults` as well, its «myself» row is the same stepper, seat 1
149
+ * being `selfKey` and seats 2, 3 these — so a second grown-up survives a
150
+ * login. `isGuestSeat` must then not claim seats 2 and up.
146
151
  */
147
152
  adults?: (seat: number) => WizardPerson & {
148
153
  adult: true;
@@ -82,6 +82,18 @@ export const WHO_SCREEN_PARTY_LABEL_KEYS = [
82
82
  'who.party.lead',
83
83
  /** Under the steppers while there are children and no grown-up. */
84
84
  'who.party.togetherHint',
85
+ /**
86
+ * A logged-in parent's grown-ups row, e.g. «Grown-ups», when
87
+ * `guestParty.adults` makes it a stepper: «myself» is seat 1, the rest are
88
+ * the guest's seats 2, 3. Without it the «myself» check row stays.
89
+ */
90
+ 'who.family.adults',
91
+ ];
92
+ /** The labels a «Voksen» stepper cannot draw without. */
93
+ const ADULT_STEPPER_LABEL_KEYS = [
94
+ 'who.party.adultFewer',
95
+ 'who.party.adultMore',
96
+ 'who.party.adultCount',
85
97
  ];
86
98
  /** The `who.party.*` keys `guestParty` cannot draw without; the two notes are extras. */
87
99
  const PARTY_REQUIRED_LABEL_KEYS = [
@@ -138,6 +150,27 @@ export function WhoScreen({ labels, format, people, family, guestChoices, addedC
138
150
  const full = seated.length >= maxPeople;
139
151
  const self = { key: selfKey, adult: true };
140
152
  const selfChecked = chosen.has(selfKey);
153
+ // «Voksne» as a number under a family too: «myself» first, then the guest's
154
+ // seats 2, 3 — a second grown-up has no profile card to tick.
155
+ const moreAdults = guestParty?.adults !== undefined &&
156
+ labels['who.family.adults'] !== undefined &&
157
+ ADULT_STEPPER_LABEL_KEYS.every((key) => labels[key] !== undefined)
158
+ ? guestParty.adults
159
+ : undefined;
160
+ const otherAdultKeys = new Set(moreAdults ? Array.from({ length: maxPeople - 1 }, (_, index) => moreAdults(index + 2).key) : []);
161
+ const isGrownUp = (person) => person.key === selfKey || otherAdultKeys.has(person.key);
162
+ const grownUps = seated.filter(isGrownUp).length;
163
+ const setGrownUps = (count) => {
164
+ if (!moreAdults)
165
+ return;
166
+ const seats = Array.from({ length: count }, (_, index) => index === 0 ? self : moreAdults(index + 1));
167
+ onChoose([...seated.filter((person) => !isGrownUp(person)), ...seats], false);
168
+ };
169
+ const partyText = (key, values = {}) => {
170
+ const label = labels[key];
171
+ return label ? fillLabel(label, values) : '';
172
+ };
173
+ const adultNote = labels['who.party.adultNote'];
141
174
  function toggle(person) {
142
175
  if (chosen.has(person.key)) {
143
176
  onChoose(seated.filter((other) => other.key !== person.key), false);
@@ -150,7 +183,7 @@ export function WhoScreen({ labels, format, people, family, guestChoices, addedC
150
183
  return (_jsxs("section", { "aria-labelledby": HEADING_ID, className: slotClass(classNames, 'root', 'space-y-6'), children: [heading, _jsxs("fieldset", { className: "space-y-4", children: [_jsx("legend", { className: "mb-2 text-sm text-muted-foreground", children: renderLabel(labels['who.family.legend'], { max: maxPeople }) }), _jsxs("ul", { className: "space-y-2", children: [family.map((entry, index) => {
151
184
  const selected = chosen.has(entry.person.key);
152
185
  return (_jsx("li", { children: _jsx(PersonCard, { name: entry.person.name ?? '', line: entry.line, index: index, selected: selected, disabled: !selected && full, describedBy: !selected && full ? LIMIT_ID : undefined, onToggle: () => toggle(entry.person), ...cardClasses }) }, entry.person.key));
153
- }), _jsx("li", { children: sheet(full, true) })] }), _jsxs(CheckRow, { checked: selfChecked, disabled: !selfChecked && full, describedBy: !selfChecked && full ? LIMIT_ID : undefined, onToggle: () => toggle(self), className: cn('h-14 justify-between px-5 text-sm', classNames?.card), selectedClassName: classNames?.cardSelected, children: [_jsx("span", { className: "font-medium", children: labels['who.family.self'] }), _jsx(CheckCircle, { checked: selfChecked })] }), _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 }) : '' })] })] }));
186
+ }), _jsx("li", { children: sheet(full, true) })] }), moreAdults ? (_jsx("div", { className: cn('rounded-lg border border-border bg-card', classNames?.card), children: _jsx(StepperRow, { title: partyText('who.family.adults'), note: adultNote ? renderLabel(adultNote) : null, value: grownUps, countLabel: partyText('who.party.adultCount', { count: grownUps }), fewerLabel: partyText('who.party.adultFewer'), moreLabel: partyText('who.party.adultMore'), canFewer: grownUps > 0, canMore: !full, limitId: full ? LIMIT_ID : undefined, onFewer: () => setGrownUps(grownUps - 1), onMore: () => setGrownUps(grownUps + 1) }) })) : (_jsxs(CheckRow, { checked: selfChecked, disabled: !selfChecked && full, describedBy: !selfChecked && full ? LIMIT_ID : undefined, onToggle: () => toggle(self), className: cn('h-14 justify-between px-5 text-sm', classNames?.card), selectedClassName: classNames?.cardSelected, children: [_jsx("span", { className: "font-medium", children: labels['who.family.self'] }), _jsx(CheckCircle, { checked: selfChecked })] })), _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 }) : '' })] })] }));
154
187
  }
155
188
  /**
156
189
  * A guest's party: how many children (a stepper) and whether the grown-up
@@ -161,9 +194,7 @@ export function WhoScreen({ labels, format, people, family, guestChoices, addedC
161
194
  */
162
195
  function GuestParty({ party, people, maxPeople, labels, classNames, onChoose, }) {
163
196
  const adultStepper = party.adults !== undefined &&
164
- labels['who.party.adultFewer'] !== undefined &&
165
- labels['who.party.adultMore'] !== undefined &&
166
- labels['who.party.adultCount'] !== undefined;
197
+ ADULT_STEPPER_LABEL_KEYS.every((key) => labels[key] !== undefined);
167
198
  const adultSeat = (seat) => seat === 1 || !party.adults ? party.adult : party.adults(seat);
168
199
  const adultKeys = new Set(Array.from({ length: maxPeople }, (_, index) => adultSeat(index + 1).key));
169
200
  const isAdultSeat = (person) => adultKeys.has(person.key);
@@ -106,13 +106,11 @@ export function ContextRail({ appId, module, hidden = false, collapsible = true,
106
106
  setDisplayWidth(null);
107
107
  ctx.contextRail.setWidth(w);
108
108
  };
109
- // Only animate width during collapse/expand toggles, not during manual
110
- // pointer drag on the ResizeHandle. displayWidth is non-null only while
111
- // the user is mid-drag — using it as the drag signal suppresses the
112
- // transition then so the rail snaps to the cursor instead of lagging
113
- // behind it.
114
- const isDragging = displayWidth !== null;
115
- return (_jsxs("aside", { id: railId, "data-testid": "context-rail", "aria-label": module.label, className: cn('group/rail relative h-full shrink-0 bg-shell-context', !isDragging && 'transition-[width] duration-200 ease-in-out motion-reduce:transition-none', collapsed && 'w-0', className), style: { width: collapsed ? 0 : width }, children: [collapsible && (_jsx(ContextRailSeam, { railId: railId, currentWidth: width, onResize: handleResize, onCommit: handleCommit })), _jsxs("div", { className: "flex h-full min-w-0 flex-col overflow-hidden", "aria-hidden": collapsed, inert: collapsed || undefined, children: [showHeader && (_jsxs("div", { className: "shrink-0 border-b border-shell-border px-4 py-3", children: [_jsx("h2", { className: "text-sm font-semibold text-foreground", children: module.label }), module.description && (_jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: module.description }))] })), _jsxs("div", { "data-meda-context-rail-scroll-area": "", className: cn('min-h-0 flex-1', scroll === 'auto' ? 'overflow-y-auto overflow-x-hidden' : 'overflow-hidden'), children: [items.length > 0 && (_jsx("nav", { "aria-label": `${module.label} navigation`, className: "flex flex-col gap-0.5 p-2", children: items.map((item) => {
109
+ // The rail never animates its width. Collapse/expand is mostly ⌘B, a
110
+ // keyboard action used many times a day, and a width transition re-wraps
111
+ // the whole main column on every frame. It snaps on the keyboard, on a
112
+ // click and while dragging.
113
+ return (_jsxs("aside", { id: railId, "data-testid": "context-rail", "aria-label": module.label, className: cn('group/rail relative h-full shrink-0 bg-shell-context', collapsed && 'w-0', className), style: { width: collapsed ? 0 : width }, children: [collapsible && (_jsx(ContextRailSeam, { railId: railId, currentWidth: width, onResize: handleResize, onCommit: handleCommit })), _jsxs("div", { className: "flex h-full min-w-0 flex-col overflow-hidden", "aria-hidden": collapsed, inert: collapsed || undefined, children: [showHeader && (_jsxs("div", { className: "shrink-0 border-b border-shell-border px-4 py-3", children: [_jsx("h2", { className: "text-sm font-semibold text-foreground", children: module.label }), module.description && (_jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: module.description }))] })), _jsxs("div", { "data-meda-context-rail-scroll-area": "", className: cn('min-h-0 flex-1', scroll === 'auto' ? 'overflow-y-auto overflow-x-hidden' : 'overflow-hidden'), children: [items.length > 0 && (_jsx("nav", { "aria-label": `${module.label} navigation`, className: "flex flex-col gap-0.5 p-2", children: items.map((item) => {
116
114
  const isActive = item.id === activeItemId;
117
115
  const klass = cn('flex items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors', isActive
118
116
  ? 'bg-primary/10 text-primary'
@@ -25,7 +25,8 @@ export { AppTabs, PanelToggle, ShellHeader, WorkspaceSwitcher } from './shell-he
25
25
  export { ShellMain } from './shell-main.js';
26
26
  export type { MedaShellProviderProps } from './shell-provider.js';
27
27
  export { MedaShellProvider, useMedaShell, useShellSelection } from './shell-provider.js';
28
+ export { ShellViewportHintProvider, type ShellViewportHintProviderProps, } from './shell-viewport-hint.js';
28
29
  export { DefaultThemeProvider, ThemeToggle, useTheme } from './theme.js';
29
30
  export { NextThemesAdapter } from './theme-next-themes.js';
30
31
  export type { AppDefinition, AppShellAppTabsConfig, AppShellAuthBranding, AppShellAuthConfig, AppShellContextRailConfig, AppShellIconRailConfig, AppShellMobileNavConfig, AppShellRightPanelConfig, AppShellVariant, AppShellWorkspaceConfig, AppTabRenderLinkArgs, CommandDefinition, ContextItem, ContextModule, ContextRailHeader, ContextRailScroll, MobileBottomNavItem, MobileDockItem, MobileNavGroup, MobileNavItem, MobileNavLinkArgs, MobileNavTree, MobileNavView, PanelMode, PanelView, ShellHeaderLayout, ShellLinkRenderArgs, ShellMainLayout, ShellRenderContext, ShellViewport, ThemeAdapter, WorkspaceDefinition, WorkspaceMenuItem, } from './types.js';
31
- export { useShellViewport } from './use-shell-viewport.js';
32
+ export { ShellViewportHintContext, useShellViewport } from './use-shell-viewport.js';
@@ -28,7 +28,8 @@ export { AppTabs, PanelToggle, ShellHeader, WorkspaceSwitcher } from './shell-he
28
28
  export { ShellMain } from './shell-main.js';
29
29
  // Provider + hooks
30
30
  export { MedaShellProvider, useMedaShell, useShellSelection } from './shell-provider.js';
31
+ export { ShellViewportHintProvider, } from './shell-viewport-hint.js';
31
32
  // Theme
32
33
  export { DefaultThemeProvider, ThemeToggle, useTheme } from './theme.js';
33
34
  export { NextThemesAdapter } from './theme-next-themes.js';
34
- export { useShellViewport } from './use-shell-viewport.js';
35
+ export { ShellViewportHintContext, useShellViewport } from './use-shell-viewport.js';
@@ -138,7 +138,11 @@ export function RightPanel({ panelViews = EMPTY_PANEL_VIEWS, defaultView, modes
138
138
  setDisplayWidth(null);
139
139
  setWidth(w);
140
140
  };
141
- return (_jsx("aside", { "data-meda-panel-mode": mode, "aria-hidden": mode === 'closed' ? 'true' : undefined, className: cn('relative h-full shrink-0 overflow-hidden border-l border-shell-border bg-shell-panel', 'transition-[width] ease-[var(--motion-ease)] duration-[var(--motion-panel)]', mode === 'fullscreen' && 'fixed inset-0 h-screen w-screen border-none', zIndexClass, className), style: widthStyle, children: mode !== 'closed' && (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-shell-border px-3 py-2", children: [_jsx("div", { className: "flex items-center gap-1", children: resolvedPanelViews.map((view) => {
141
+ return (_jsx("aside", { "data-meda-panel-mode": mode, "aria-hidden": mode === 'closed' ? 'true' : undefined,
142
+ // No width transition: ⌘I / ⌘E are keyboard actions used many times a
143
+ // day, and an animated width re-wraps the main column every frame (and
144
+ // lags behind the cursor while resizing). The panel snaps.
145
+ className: cn('relative h-full shrink-0 overflow-hidden border-l border-shell-border bg-shell-panel', mode === 'fullscreen' && 'fixed inset-0 h-screen w-screen border-none', zIndexClass, className), style: widthStyle, children: mode !== 'closed' && (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-shell-border px-3 py-2", children: [_jsx("div", { className: "flex items-center gap-1", children: resolvedPanelViews.map((view) => {
142
146
  const isActive = view.id === activeView;
143
147
  const Icon = view.icon;
144
148
  const children = (_jsxs(_Fragment, { children: [_jsx(Icon, { size: 14, "aria-hidden": "true" }), _jsx("span", { children: view.label })] }));
@@ -0,0 +1,34 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { ShellViewport } from './types.js';
3
+ export interface ShellViewportHintProviderProps {
4
+ /**
5
+ * Coarse device class detected from the request, or `null` to keep the
6
+ * desktop-first default.
7
+ */
8
+ value: ShellViewport | null;
9
+ children: ReactNode;
10
+ }
11
+ /**
12
+ * Client wrapper that supplies the SSR viewport hint.
13
+ *
14
+ * React Context providers cannot be rendered from a React Server Component,
15
+ * so an App Router server component — the place where request headers are
16
+ * actually available — must render this client component instead of touching
17
+ * `ShellViewportHintContext.Provider` itself:
18
+ *
19
+ * // app/layout.tsx — server component
20
+ * import { headers } from 'next/headers';
21
+ * import { ShellViewportHintProvider } from '@medalsocial/meda/shell';
22
+ *
23
+ * export default async function Layout({ children }) {
24
+ * const isPhone = (await headers()).get('sec-ch-ua-mobile') === '?1';
25
+ * return (
26
+ * <ShellViewportHintProvider value={isPhone ? 'mobile' : null}>
27
+ * {children}
28
+ * </ShellViewportHintProvider>
29
+ * );
30
+ * }
31
+ *
32
+ * See {@link ShellViewportHintContext} for what the hint does.
33
+ */
34
+ export declare function ShellViewportHintProvider({ value, children }: ShellViewportHintProviderProps): import("react").JSX.Element;
@@ -0,0 +1,29 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { ShellViewportHintContext } from './use-shell-viewport.js';
4
+ /**
5
+ * Client wrapper that supplies the SSR viewport hint.
6
+ *
7
+ * React Context providers cannot be rendered from a React Server Component,
8
+ * so an App Router server component — the place where request headers are
9
+ * actually available — must render this client component instead of touching
10
+ * `ShellViewportHintContext.Provider` itself:
11
+ *
12
+ * // app/layout.tsx — server component
13
+ * import { headers } from 'next/headers';
14
+ * import { ShellViewportHintProvider } from '@medalsocial/meda/shell';
15
+ *
16
+ * export default async function Layout({ children }) {
17
+ * const isPhone = (await headers()).get('sec-ch-ua-mobile') === '?1';
18
+ * return (
19
+ * <ShellViewportHintProvider value={isPhone ? 'mobile' : null}>
20
+ * {children}
21
+ * </ShellViewportHintProvider>
22
+ * );
23
+ * }
24
+ *
25
+ * See {@link ShellViewportHintContext} for what the hint does.
26
+ */
27
+ export function ShellViewportHintProvider({ value, children }) {
28
+ return (_jsx(ShellViewportHintContext.Provider, { value: value, children: children }));
29
+ }
@@ -1,4 +1,6 @@
1
1
  import type { ShellViewport } from './types.js';
2
+ /** Optional request-derived band for server rendering and hydration only. */
3
+ export declare const ShellViewportHintContext: import("react").Context<ShellViewport | null>;
2
4
  /**
3
5
  * The shell's viewport band, resolved SYNCHRONOUSLY on the first client
4
6
  * render.
@@ -12,8 +14,8 @@ import type { ShellViewport } from './types.js';
12
14
  *
13
15
  * `useSyncExternalStore` reads `matchMedia` during the first client render,
14
16
  * so client-rendered apps commit the correct tree once. Server rendering
15
- * still returns 'desktop' via the server snapshot, and hydration stays
16
- * mismatch-safe (React uses the server snapshot while hydrating, then
17
- * re-reads — SSR consumers keep exactly the old behavior).
17
+ * uses the optional request-derived hint, otherwise 'desktop'. Hydration uses
18
+ * that same snapshot before switching to the live viewport. A hint lets an
19
+ * app send mobile shell HTML before its JavaScript loads.
18
20
  */
19
21
  export declare function useShellViewport(): ShellViewport;
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { useSyncExternalStore } from 'react';
2
+ import { createContext, useContext, useSyncExternalStore } from 'react';
3
3
  const BREAKPOINTS = {
4
4
  mobile: '(max-width: 767px)',
5
5
  tablet: '(min-width: 768px) and (max-width: 1023px)',
@@ -7,6 +7,8 @@ const BREAKPOINTS = {
7
7
  wide: '(min-width: 1280px) and (max-width: 1535px)',
8
8
  ultrawide: '(min-width: 1536px)',
9
9
  };
10
+ /** Optional request-derived band for server rendering and hydration only. */
11
+ export const ShellViewportHintContext = createContext(null);
10
12
  function getMatchMedia() {
11
13
  /* v8 ignore next — SSR guard: window is always defined in jsdom test env */
12
14
  if (typeof window === 'undefined')
@@ -63,7 +65,6 @@ function getClientViewport() {
63
65
  // First render happens before subscribe — detect directly.
64
66
  return detectViewport();
65
67
  }
66
- const getServerViewport = () => 'desktop';
67
68
  /**
68
69
  * The shell's viewport band, resolved SYNCHRONOUSLY on the first client
69
70
  * render.
@@ -77,10 +78,11 @@ const getServerViewport = () => 'desktop';
77
78
  *
78
79
  * `useSyncExternalStore` reads `matchMedia` during the first client render,
79
80
  * so client-rendered apps commit the correct tree once. Server rendering
80
- * still returns 'desktop' via the server snapshot, and hydration stays
81
- * mismatch-safe (React uses the server snapshot while hydrating, then
82
- * re-reads — SSR consumers keep exactly the old behavior).
81
+ * uses the optional request-derived hint, otherwise 'desktop'. Hydration uses
82
+ * that same snapshot before switching to the live viewport. A hint lets an
83
+ * app send mobile shell HTML before its JavaScript loads.
83
84
  */
84
85
  export function useShellViewport() {
85
- return useSyncExternalStore(subscribeToViewport, getClientViewport, getServerViewport);
86
+ const hint = useContext(ShellViewportHintContext);
87
+ return useSyncExternalStore(subscribeToViewport, getClientViewport, () => hint ?? 'desktop');
86
88
  }
@@ -1,4 +1,4 @@
1
- /*! Vendored from @xyflow/react@12.11.6 dist/style.css — MIT License, (c) webkid GmbH. https://github.com/xyflow/xyflow */
1
+ /*! Vendored from @xyflow/react@12.12.0 dist/style.css — MIT License, (c) webkid GmbH. https://github.com/xyflow/xyflow */
2
2
  /* this gets exported as style.css and can be used for the default theming */
3
3
  /* these are the necessary styles for React/Svelte Flow, they get used by base.css and style.css */
4
4
  .react-flow {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medalsocial/meda",
3
- "version": "3.12.0",
3
+ "version": "3.14.0",
4
4
  "description": "Shared Meda UI shell and runtime package.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -210,7 +210,7 @@
210
210
  "vaul": "^1.1.2"
211
211
  },
212
212
  "devDependencies": {
213
- "@biomejs/biome": "2.5.14",
213
+ "@biomejs/biome": "2.5.15",
214
214
  "@changesets/changelog-github": "1.0.1",
215
215
  "@changesets/cli": "3.0.3",
216
216
  "@dnd-kit/core": "6.3.1",
@@ -218,48 +218,48 @@
218
218
  "@dnd-kit/utilities": "3.2.2",
219
219
  "@fontsource-variable/geist": "5.3.0",
220
220
  "@fontsource-variable/geist-mono": "5.3.0",
221
- "@react-three/fiber": "9.7.0",
222
- "@size-limit/preset-small-lib": "13.0.3",
223
- "@storybook/addon-a11y": "10.6.0",
224
- "@storybook/addon-docs": "10.6.0",
225
- "@storybook/addon-mcp": "10.6.0",
226
- "@storybook/addon-themes": "10.6.0",
227
- "@storybook/addon-vitest": "10.6.0",
228
- "@storybook/react-vite": "10.6.0",
221
+ "@react-three/fiber": "9.8.1",
222
+ "@size-limit/preset-small-lib": "13.1.1",
223
+ "@storybook/addon-a11y": "10.6.1",
224
+ "@storybook/addon-docs": "10.6.1",
225
+ "@storybook/addon-mcp": "10.6.1",
226
+ "@storybook/addon-themes": "10.6.1",
227
+ "@storybook/addon-vitest": "10.6.1",
228
+ "@storybook/react-vite": "10.6.1",
229
229
  "@tailwindcss/cli": "4.3.3",
230
230
  "@tailwindcss/vite": "4.3.3",
231
- "@tanstack/intent": "0.4.0",
231
+ "@tanstack/intent": "0.5.5",
232
232
  "@testing-library/jest-dom": "7.0.1",
233
233
  "@testing-library/react": "16.3.3",
234
234
  "@types/react": "19.3.0",
235
235
  "@types/react-dom": "19.3.0",
236
236
  "@types/three": "0.185.4",
237
- "@vitejs/plugin-react": "6.1.1",
237
+ "@vitejs/plugin-react": "6.1.2",
238
238
  "@vitest/browser-playwright": "4.1.11",
239
239
  "@vitest/coverage-istanbul": "4.1.11",
240
240
  "@vitest/coverage-v8": "4.1.11",
241
- "@xyflow/react": "12.11.6",
242
- "chromatic": "18.9.4",
241
+ "@xyflow/react": "12.12.0",
242
+ "chromatic": "18.12.0",
243
243
  "esbuild": "0.28.2",
244
244
  "fast-check": "4.10.2",
245
245
  "husky": "9.1.7",
246
- "jsdom": "30.1.0",
246
+ "jsdom": "30.1.2",
247
247
  "next-themes": "0.4.6",
248
- "playwright": "1.63.0",
248
+ "playwright": "1.64.0",
249
249
  "react": "19.2.8",
250
250
  "react-dom": "19.2.8",
251
251
  "react-markdown": "10.1.0",
252
252
  "rehype-highlight": "7.0.2",
253
253
  "remark-gfm": "4.0.1",
254
- "size-limit": "13.0.3",
255
- "storybook": "10.6.0",
254
+ "size-limit": "13.1.1",
255
+ "storybook": "10.6.1",
256
256
  "tailwindcss": "4.3.3",
257
- "three": "0.185.1",
257
+ "three": "0.186.1",
258
258
  "typescript": "~6.0.3",
259
- "vite": "8.3.0",
259
+ "vite": "8.3.4",
260
260
  "vitest": "4.1.11",
261
261
  "vitest-axe": "0.1.0",
262
- "wrangler": "4.135.0"
262
+ "wrangler": "4.149.0"
263
263
  },
264
264
  "keywords": [
265
265
  "tanstack-intent"