@leabankab/component-library 8.4.0 → 8.6.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.
- package/CHANGELOG.md +17 -0
- package/dist/dev-panel/dev-panel-payload.d.ts +30 -6
- package/dist/dev-panel/dev-panel.d.ts +37 -6
- package/dist/dev-panel/parts.d.ts +1 -1
- package/dist/dev-panel/use-dev-panel-labels.d.ts +1 -1
- package/dist/dev-panel.cjs +2 -2
- package/dist/dev-panel.cjs.map +1 -1
- package/dist/dev-panel.css +1 -1
- package/dist/dev-panel.mjs +566 -409
- package/dist/dev-panel.mjs.map +1 -1
- package/package.json +1 -1
package/dist/dev-panel.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-panel.mjs","names":[],"sources":["../src/dev-panel/dev-panel.module.css","../src/dev-panel/use-dev-panel-labels.ts","../src/dev-panel/dev-panel.tsx","../src/dev-panel/use-roving-radio.ts","../src/dev-panel/segmented-control.tsx","../src/dev-panel/dev-panel-country.tsx","../src/dev-panel/dev-panel-variant.tsx","../src/dev-panel/dev-panel-step-state.tsx","../src/dev-panel/dev-panel-steps.tsx","../src/dev-panel/dev-panel-status.tsx","../src/dev-panel/use-copy.ts","../src/dev-panel/dev-panel-flow.tsx","../src/dev-panel/json-highlight.tsx","../src/dev-panel/dev-panel-payload.tsx","../src/dev-panel/dev-panel-footer.tsx","../src/dev-panel/parts.ts"],"sourcesContent":["/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so the panel renders correctly without a provider.\n *\n * One stylesheet for every part of the dev panel. The panel is a tool for the\n * people building a form app and has a look of its own: it is denser than the\n * components in src/components/, so several of its distances sit between two\n * steps of the spacing scale (0.25, 0.75, 1.25 and 1.75 steps), and its small\n * labels are uppercase. Neither is a precedent for a component.\n *\n * A state is read from the attribute that announces it (`aria-checked`,\n * `aria-expanded`) where there is one, so the look cannot disagree with what\n * assistive tech is told.\n */\n\n/*\n * The small text of the panel is 12px, one pixel below the smallest step of\n * the type scale (--lea-font-step-minus-2, 13px). At 13px a long step label\n * (\"Civilinformation\") no longer fits its cell in the grid of four and breaks\n * one letter from its end.\n */\n.Badge,\n.SectionLabel,\n.SectionMeta,\n.FlowLabel,\n.CountryCode,\n.StepLabel,\n.PayloadCode {\n font-size: 0.75rem;\n}\n\n/* ----------------------------------------------------------------------------\n * The panel\n * ------------------------------------------------------------------------- */\n\n/* The outer card: a rounded, cream surface with a subtle border. */\n.DevPanel {\n display: flex;\n overflow: hidden;\n width: 100%;\n /* 460px, the width the panel is drawn at. It follows a narrower container. */\n max-width: 28.75rem;\n flex-direction: column;\n border: 1px solid var(--lea-palette-border-subtle);\n border-radius: var(--lea-radius-4xl);\n background-color: var(--lea-color-beige-50);\n color: var(--lea-palette-text-base);\n font-family: inherit;\n}\n\n/* The chrome strip: title and status dot, an optional badge, the minimise control. */\n.Header {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n padding-block: calc(var(--lea-spacing) * 1.75);\n padding-inline: var(--lea-spacing-2-5);\n border-bottom: 1px solid var(--lea-palette-border-subtle);\n background-color: var(--lea-color-beige-100);\n gap: var(--lea-spacing-1-5);\n}\n\n.HeaderTitle {\n display: inline-flex;\n align-items: center;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-font-step-0);\n font-weight: var(--lea-font-weight-semibold);\n gap: var(--lea-spacing-1);\n}\n\n/* The hide and minimise buttons, side by side. */\n.HeaderActions {\n display: inline-flex;\n align-items: center;\n gap: var(--lea-spacing-1);\n}\n\n/* The small \"live\" indicator dot beside the panel title. */\n.HeaderDot {\n width: var(--lea-spacing-1);\n height: var(--lea-spacing-1);\n flex-shrink: 0;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-tertiary-base);\n}\n\n/* The \"MOCK\" / \"live\" pill next to a title. */\n.Badge {\n display: inline-flex;\n align-items: center;\n padding-block: calc(var(--lea-spacing) * 0.25);\n padding-inline: calc(var(--lea-spacing) * 0.75);\n border-radius: var(--lea-radius-sm);\n background-color: var(--lea-color-neutral-200);\n color: var(--lea-color-neutral-700);\n font-weight: var(--lea-font-weight-semibold);\n letter-spacing: 0.08em;\n line-height: var(--lea-line-height-snug);\n text-transform: uppercase;\n}\n\n/* The same pill in the header of the dark side flyout. */\n.Badge[data-on-dark] {\n background-color: var(--lea-color-neutral-800);\n color: var(--lea-color-neutral-300);\n}\n\n/*\n * The tab that brings a hidden panel back (`DevPanel.Handle`): the header of\n * the panel, cut down to its dot and title, fixed to the bottom of the edge of\n * the window. It floats over the app, so it has the shadow the panel's own\n * flyout has, a size smaller. 1300 is the layer of the option lists, above an\n * app's own content.\n */\n.Handle {\n position: fixed;\n z-index: 1300;\n bottom: var(--lea-spacing-2);\n display: inline-flex;\n align-items: center;\n padding-block: var(--lea-spacing-1);\n padding-inline: var(--lea-spacing-1-5);\n border: 1px solid var(--lea-palette-border-subtle);\n margin: 0;\n background-color: var(--lea-color-beige-100);\n box-shadow: var(--lea-shadow-md);\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--lea-text-label-md-font-size);\n /* Semibold, as the title in the header it stands for. */\n font-weight: var(--lea-font-weight-semibold);\n gap: var(--lea-spacing-1);\n letter-spacing: var(--lea-text-label-md-letter-spacing);\n line-height: var(--lea-text-label-md-line-height);\n}\n\n/* Flush with the edge: no border and no rounding on that side. */\n.Handle[data-side=\"right\"] {\n right: 0;\n border-inline-end: 0;\n border-radius: var(--lea-radius-xl) 0 0 var(--lea-radius-xl);\n}\n\n.Handle[data-side=\"left\"] {\n left: 0;\n border-inline-start: 0;\n border-radius: 0 var(--lea-radius-xl) var(--lea-radius-xl) 0;\n}\n\n@media (hover: hover) {\n .Handle:hover {\n background-color: var(--lea-color-beige-200);\n }\n}\n\n.Handle:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n/*\n * A hideable panel, minimised, on a narrow screen: a small round button and\n * nothing more, so it does not cover the form. The title stays for screen\n * readers; the hide button goes, as there is nothing left to hide.\n */\n@media (max-width: 599.95px) {\n .DevPanel[data-hideable][data-minimised] {\n width: auto;\n border-radius: var(--lea-radius-full);\n box-shadow: var(--lea-shadow-md);\n }\n\n .DevPanel[data-hideable][data-minimised] .Header {\n padding: var(--lea-spacing-0-5);\n border-bottom: 0;\n }\n\n .DevPanel[data-hideable][data-minimised] .HeaderTitle {\n position: absolute;\n overflow: hidden;\n width: 1px;\n height: 1px;\n clip-path: inset(50%);\n white-space: nowrap;\n }\n\n .DevPanel[data-hideable][data-minimised] .HideButton {\n display: none;\n }\n}\n\n/* The body that holds the sections. It scrolls when the app limits the height. */\n.Body {\n display: flex;\n flex-direction: column;\n padding: var(--lea-spacing-2-5);\n gap: var(--lea-spacing-2-5);\n overflow-y: auto;\n}\n\n/* ----------------------------------------------------------------------------\n * A labelled section (COUNTRY, VARIANT, …)\n * ------------------------------------------------------------------------- */\n\n.Section {\n display: flex;\n flex-direction: column;\n gap: calc(var(--lea-spacing) * 1.25);\n}\n\n.SectionHeaderRow {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: var(--lea-spacing-1);\n}\n\n/*\n * The uppercase section heading. neutral-600 rather than text-subtle, so the\n * small label clears 4.5:1 against the cream body, as the `Tabs` track does.\n */\n.SectionLabel {\n margin: 0;\n color: var(--lea-color-neutral-600);\n font-weight: var(--lea-font-weight-semibold);\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n/* Optional trailing meta on a section heading row (e.g. the `6 / 10` count). */\n.SectionMeta {\n color: var(--lea-color-neutral-600);\n font-weight: var(--lea-font-weight-medium);\n}\n\n/* ----------------------------------------------------------------------------\n * The flow row\n * ------------------------------------------------------------------------- */\n\n.Flow {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n padding-block: var(--lea-spacing-1);\n padding-inline: var(--lea-spacing-1-5);\n border-radius: var(--lea-radius-lg);\n background-color: var(--lea-color-beige-100);\n gap: var(--lea-spacing-1);\n}\n\n.FlowText {\n display: inline-flex;\n min-width: 0;\n align-items: center;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-font-step-minus-1);\n gap: calc(var(--lea-spacing) * 0.75);\n}\n\n/* The label and the breadcrumb each stay on one line and end in an ellipsis. */\n.FlowText > span {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.FlowLabel {\n flex-shrink: 0;\n color: var(--lea-color-neutral-600);\n font-weight: var(--lea-font-weight-semibold);\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n/* The dot between two parts of the breadcrumb. */\n.FlowSeparator {\n margin-inline: var(--lea-spacing-0-5);\n color: var(--lea-palette-text-muted);\n}\n\n/* ----------------------------------------------------------------------------\n * Segmented single-select (Country, Variant, Step state)\n * ------------------------------------------------------------------------- */\n\n/*\n * The beige track that holds the options. It scrolls sideways when they do\n * not fit, with the scrollbar hidden so the row stays a clean pill.\n */\n.SegmentedGroup {\n display: flex;\n max-width: 100%;\n flex-flow: row nowrap;\n align-items: stretch;\n padding: var(--lea-spacing-0-5);\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-bg-muted);\n gap: var(--lea-spacing-0-5);\n overflow-x: auto;\n scrollbar-width: none;\n}\n\n.SegmentedGroup::-webkit-scrollbar {\n display: none;\n}\n\n/*\n * The \"pill\" variant used by Country and Variant: the options share the track\n * evenly and the chosen one flips to a white pill.\n */\n.SegmentedOption {\n position: relative;\n display: inline-flex;\n min-width: 0;\n flex: 1 1 0;\n align-items: center;\n justify-content: center;\n padding-block: var(--lea-spacing-1);\n padding-inline: var(--lea-spacing-1-5);\n border: none;\n border-radius: var(--lea-radius-full);\n margin: 0;\n background-color: transparent;\n box-shadow: none;\n color: var(--lea-color-neutral-600);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-medium);\n gap: calc(var(--lea-spacing) * 0.75);\n line-height: var(--lea-line-height-none);\n transition:\n background-color var(--lea-duration-normal) var(--lea-easing-standard),\n color var(--lea-duration-normal) var(--lea-easing-standard),\n box-shadow var(--lea-duration-normal) var(--lea-easing-standard);\n user-select: none;\n white-space: nowrap;\n}\n\n.SegmentedOption[aria-checked=\"true\"] {\n background-color: var(--lea-palette-bg-base);\n /* A hairline of shade under the pill, in the beige of the track. */\n box-shadow: 0 1px 2px var(--lea-color-beige-300);\n color: var(--lea-palette-text-base);\n font-weight: var(--lea-font-weight-semibold);\n}\n\n.SegmentedOption svg {\n display: block;\n flex-shrink: 0;\n}\n\n/* The short code before a country name (e.g. `SE`). */\n.CountryCode {\n color: var(--lea-color-neutral-600);\n font-weight: var(--lea-font-weight-semibold);\n}\n\n/*\n * The \"chip\" variant used by Step state: pills that hug their content, wrap\n * onto more lines and carry a small leading dot.\n */\n.ChipGroup {\n display: flex;\n flex-flow: row wrap;\n gap: var(--lea-spacing-1);\n}\n\n.Chip {\n display: inline-flex;\n align-items: center;\n padding-block: calc(var(--lea-spacing) * 0.75);\n padding-inline: var(--lea-spacing-1-5);\n border: 1px solid var(--lea-palette-border-base);\n border-radius: var(--lea-radius-full);\n margin: 0;\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-medium);\n gap: calc(var(--lea-spacing) * 0.75);\n /* Between `none` and `tight`: the padding sets the height of the pill. */\n line-height: 1.2;\n transition:\n background-color var(--lea-duration-normal) var(--lea-easing-standard),\n color var(--lea-duration-normal) var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard);\n user-select: none;\n white-space: nowrap;\n}\n\n.Chip[aria-checked=\"true\"] {\n border-color: var(--lea-palette-border-accent);\n background-color: var(--lea-palette-primary-subtle);\n color: var(--lea-palette-text-accent);\n font-weight: var(--lea-font-weight-semibold);\n}\n\n/* 7px, off the spacing scale like the distances above. */\n.ChipDot {\n width: calc(var(--lea-spacing) * 0.875);\n height: calc(var(--lea-spacing) * 0.875);\n flex-shrink: 0;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-color-neutral-400);\n}\n\n.Chip[aria-checked=\"true\"] .ChipDot {\n background-color: var(--lea-palette-primary-base);\n}\n\n.SegmentedOption[data-disabled],\n.Chip[data-disabled] {\n cursor: not-allowed;\n opacity: 0.45;\n pointer-events: none;\n}\n\n/* ----------------------------------------------------------------------------\n * Jump-to-step grid\n * ------------------------------------------------------------------------- */\n\n/* The number of columns arrives inline from the `columns` prop. */\n.StepGrid {\n display: grid;\n gap: var(--lea-spacing-1-5);\n grid-template-columns: repeat(var(--_columns), minmax(0, 1fr));\n}\n\n.StepButton {\n display: flex;\n /* Lets the button shrink to its grid track, so a long label wraps instead of\n running into the next cell. */\n min-width: 0;\n flex-direction: column;\n align-items: center;\n padding: 0;\n border: none;\n border-radius: var(--lea-radius-lg);\n margin: 0;\n background: none;\n cursor: pointer;\n font-family: inherit;\n gap: calc(var(--lea-spacing) * 0.75);\n}\n\n.StepButton[data-disabled] {\n cursor: not-allowed;\n opacity: 0.5;\n pointer-events: none;\n}\n\n/* An upcoming step. The other two states set the three properties below. */\n.StepCircle {\n --step-bg: transparent;\n --step-border: var(--lea-color-neutral-400);\n --step-fg: var(--lea-color-neutral-600);\n\n position: relative;\n display: inline-flex;\n width: var(--lea-spacing-5);\n height: var(--lea-spacing-5);\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n border: 2px solid var(--step-border);\n border-radius: var(--lea-radius-full);\n background-color: var(--step-bg);\n color: var(--step-fg);\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-none);\n transition:\n background-color var(--lea-duration-normal) var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard),\n color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.StepButton[data-state=\"complete\"] .StepCircle {\n --step-bg: var(--lea-palette-tertiary-base);\n --step-border: var(--lea-palette-tertiary-base);\n --step-fg: var(--lea-palette-tertiary-contrast);\n}\n\n.StepButton[data-state=\"current\"] .StepCircle {\n --step-bg: var(--lea-palette-primary-base);\n --step-border: var(--lea-palette-primary-base);\n --step-fg: var(--lea-palette-primary-contrast);\n}\n\n.StepCircle svg {\n display: block;\n}\n\n/* The small warning dot on a step that needs attention, ringed in the body colour. */\n.StepFlagDot {\n position: absolute;\n inset-block-start: -1px;\n inset-inline-end: -1px;\n width: calc(var(--lea-spacing) * 1.25);\n height: calc(var(--lea-spacing) * 1.25);\n border: 2px solid var(--lea-color-beige-50);\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-status-warning-base);\n}\n\n.StepLabel {\n /* As wide as the cell, and a long word breaks, so labels never overlap. */\n width: 100%;\n color: var(--lea-color-neutral-600);\n font-weight: var(--lea-font-weight-regular);\n line-height: 1.2;\n overflow-wrap: anywhere;\n text-align: center;\n}\n\n.StepButton[data-state=\"current\"] .StepLabel {\n color: var(--lea-palette-text-base);\n font-weight: var(--lea-font-weight-semibold);\n}\n\n/* ----------------------------------------------------------------------------\n * Step state\n * ------------------------------------------------------------------------- */\n\n/* The name of the step in the \"Variations for …\" line. */\n.StepStateName {\n color: var(--lea-palette-text-base);\n font-weight: var(--lea-font-weight-semibold);\n}\n\n/* ----------------------------------------------------------------------------\n * Application status cards\n * ------------------------------------------------------------------------- */\n\n.StatusGrid {\n display: grid;\n gap: calc(var(--lea-spacing) * 1.25);\n grid-template-columns: repeat(2, minmax(0, 1fr));\n}\n\n/* `neutral` is the base rule; each status sets the two properties below. */\n.StatusCard {\n --status-base: var(--lea-color-neutral-400);\n --status-subtle: var(--lea-color-neutral-100);\n\n display: inline-flex;\n width: 100%;\n align-items: center;\n padding-block: calc(var(--lea-spacing) * 1.25);\n padding-inline: var(--lea-spacing-1-5);\n border: 1px solid var(--lea-palette-border-base);\n border-radius: var(--lea-radius-lg);\n margin: 0;\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-medium);\n gap: var(--lea-spacing-1);\n line-height: 1.2;\n text-align: start;\n transition:\n background-color var(--lea-duration-normal) var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.StatusCard[data-color=\"warning\"] {\n --status-base: var(--lea-palette-status-warning-base);\n --status-subtle: var(--lea-palette-status-warning-subtle);\n}\n\n.StatusCard[data-color=\"success\"] {\n --status-base: var(--lea-palette-status-success-base);\n --status-subtle: var(--lea-palette-status-success-subtle);\n}\n\n.StatusCard[data-color=\"info\"] {\n --status-base: var(--lea-palette-status-info-base);\n --status-subtle: var(--lea-palette-status-info-subtle);\n}\n\n.StatusCard[data-color=\"error\"] {\n --status-base: var(--lea-palette-status-error-base);\n --status-subtle: var(--lea-palette-status-error-subtle);\n}\n\n.StatusCard[aria-checked=\"true\"] {\n border-color: var(--status-base);\n background-color: var(--status-subtle);\n font-weight: var(--lea-font-weight-semibold);\n}\n\n.StatusCard[data-disabled] {\n cursor: not-allowed;\n opacity: 0.5;\n pointer-events: none;\n}\n\n/* Decorative: the status is always spelled out in the label beside it. */\n.StatusDot {\n width: calc(var(--lea-spacing) * 1.25);\n height: calc(var(--lea-spacing) * 1.25);\n flex-shrink: 0;\n border-radius: var(--lea-radius-full);\n background-color: var(--status-base);\n}\n\n/* ----------------------------------------------------------------------------\n * Payload preview\n * ------------------------------------------------------------------------- */\n\n.PayloadToggle {\n display: flex;\n width: 100%;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n padding-block: calc(var(--lea-spacing) * 1.25);\n padding-inline: var(--lea-spacing-1-5);\n border: none;\n border-radius: var(--lea-radius-lg);\n margin: 0;\n background-color: var(--lea-palette-bg-accent);\n color: var(--lea-palette-text-accent);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-semibold);\n gap: var(--lea-spacing-1);\n}\n\n.PayloadToggleLabel {\n display: inline-flex;\n align-items: center;\n gap: var(--lea-spacing-1);\n}\n\n/* Points right while closed and down while the panel below is open. */\n.PayloadChevron {\n display: inline-flex;\n transform: rotate(0deg);\n transition: transform var(--lea-duration-emphasis) var(--lea-easing-standard);\n}\n\n.PayloadToggle[aria-expanded=\"true\"] .PayloadChevron {\n transform: rotate(90deg);\n}\n\n/*\n * With `placement=\"side\"` it points left, to where the flyout appears beside\n * the panel, open or closed: `aria-expanded` carries the state.\n */\n.PayloadToggle[data-placement=\"side\"] .PayloadChevron {\n transform: rotate(180deg);\n transition: none;\n}\n\n.PayloadChevron svg {\n display: block;\n}\n\n/* The dark panel that opens below the toggle. */\n.PayloadPanel {\n overflow: hidden;\n border-radius: var(--lea-radius-lg);\n margin-block-start: calc(var(--lea-spacing) * 1.25);\n background-color: var(--lea-color-neutral-950);\n}\n\n/* The title row of the dark panel and of the side flyout. */\n.PayloadHeader {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n padding-block: calc(var(--lea-spacing) * 1.25);\n padding-inline: calc(var(--lea-spacing) * 1.75);\n border-bottom: 1px solid var(--lea-color-neutral-800);\n color: var(--lea-palette-text-contrast);\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-semibold);\n gap: var(--lea-spacing-1);\n}\n\n/*\n * The payload itself. Code, so it is the one place with a family of its own:\n * the monospace font of the system. 1.6 is roomier than `normal`, so the lines\n * of a long payload are easy to follow.\n */\n.PayloadCode {\n overflow: auto;\n max-height: calc(var(--lea-spacing) * 40);\n padding: calc(var(--lea-spacing) * 1.75);\n margin: 0;\n color: var(--lea-color-neutral-100);\n font-family:\n ui-monospace, SFMono-Regular, Menlo, Consolas, \"Liberation Mono\", monospace;\n line-height: 1.6;\n white-space: pre;\n}\n\n/*\n * The syntax-highlighted payload. Every shade clears 4.5:1 against\n * neutral-950, so highlighting never trades contrast for colour.\n */\n.Token[data-token=\"key\"] {\n color: var(--lea-color-neutral-100);\n}\n\n.Token[data-token=\"string\"] {\n color: var(--lea-color-teal-300);\n}\n\n.Token[data-token=\"number\"] {\n color: var(--lea-color-orange-300);\n}\n\n.Token[data-token=\"boolean\"] {\n color: var(--lea-color-blue-300);\n}\n\n.Token[data-token=\"null\"] {\n color: var(--lea-color-purple-300);\n}\n\n.Token[data-token=\"punctuation\"] {\n color: var(--lea-color-neutral-400);\n}\n\n/* ----------------------------------------------------------------------------\n * Payload side flyout (`placement=\"side\"`)\n * ------------------------------------------------------------------------- */\n\n/*\n * The floating dark panel that opens beside the dev panel. It is rendered in\n * a portal on the body and placed inline (top, left, width, height) from the\n * rect of the panel, so it can sit outside the panel's clipped bounds. The\n * layer is the one `Dialog` and `Drawer` use.\n */\n.Flyout {\n position: fixed;\n z-index: 1300;\n display: flex;\n overflow: hidden;\n flex-direction: column;\n border-radius: var(--lea-radius-4xl);\n background-color: var(--lea-color-neutral-950);\n box-shadow: var(--lea-shadow-2xl);\n color: var(--lea-palette-text-contrast);\n /* It takes focus only when nothing inside it can; the dialog itself has no ring. */\n outline: none;\n}\n\n.FlyoutTitle {\n display: inline-flex;\n min-width: 0;\n align-items: center;\n gap: var(--lea-spacing-1);\n}\n\n/* The payload in the flyout fills the height that is left and scrolls. */\n.Flyout .PayloadCode {\n min-height: 0;\n max-height: none;\n flex: 1;\n}\n\n.FlyoutFooter {\n padding: var(--lea-spacing-1-5);\n border-top: 1px solid var(--lea-color-neutral-800);\n background-color: var(--lea-color-neutral-900);\n}\n\n/* The full-width \"Copy JSON\" action at the foot of the flyout. */\n.FlyoutCopyButton {\n display: inline-flex;\n width: 100%;\n align-items: center;\n justify-content: center;\n padding-block: calc(var(--lea-spacing) * 1.25);\n padding-inline: var(--lea-spacing-1-5);\n border: 1px solid var(--lea-color-neutral-700);\n border-radius: var(--lea-radius-lg);\n margin: 0;\n background-color: var(--lea-color-neutral-800);\n color: var(--lea-palette-text-contrast);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-semibold);\n gap: var(--lea-spacing-1);\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard);\n}\n\n.FlyoutCopyButton svg {\n display: block;\n}\n\n@media (hover: hover) {\n .FlyoutCopyButton:hover {\n background-color: var(--lea-color-neutral-700);\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Footer actions\n * ------------------------------------------------------------------------- */\n\n/*\n * The action row. The negative margins cancel the padding of `.Body`, so as\n * the last child the footer sits flush with the edges of the panel under a\n * full-width divider.\n */\n.Footer {\n display: flex;\n flex-flow: row wrap;\n align-items: center;\n padding-block: var(--lea-spacing-2);\n padding-inline: var(--lea-spacing-2-5);\n border-top: 1px solid var(--lea-palette-border-subtle);\n margin-block: var(--lea-spacing-0-5) calc(var(--lea-spacing-2-5) * -1);\n margin-inline: calc(var(--lea-spacing-2-5) * -1);\n background-color: var(--lea-color-beige-100);\n gap: calc(var(--lea-spacing) * 1.25);\n}\n\n/* ----------------------------------------------------------------------------\n * Shared\n * ------------------------------------------------------------------------- */\n\n.SegmentedOption:focus-visible,\n.Chip:focus-visible,\n.StepButton:focus-visible,\n.StatusCard:focus-visible,\n.PayloadToggle:focus-visible,\n.FlyoutCopyButton:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n}\n\n/*\n * On the dark payload panels the ring is white: text-base does not show on\n * neutral-950. `.OnDark` is on the library's `IconButton`s in those panels,\n * and the panel class in front lifts the rule above the button's own ring,\n * whichever stylesheet the app happens to load last.\n */\n.FlyoutCopyButton:focus-visible,\n.PayloadPanel .OnDark:focus-visible,\n.Flyout .OnDark:focus-visible {\n outline-color: var(--lea-palette-text-contrast);\n}\n\n/* Text for assistive tech only: the live regions that confirm a copy. */\n.VisuallyHidden {\n position: absolute;\n overflow: hidden;\n width: 1px;\n height: 1px;\n padding: 0;\n border: 0;\n margin: -1px;\n clip: rect(0 0 0 0);\n white-space: nowrap;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .SegmentedOption,\n .Chip,\n .StepCircle,\n .StatusCard,\n .PayloadChevron,\n .FlyoutCopyButton {\n transition-duration: 0.01ms;\n }\n}\n","import { createTranslation } from \"../index\";\n\n/**\n * Translated accessible names and status words used across the `DevPanel`\n * sub-components (copy buttons, the minimise control, and the per-step status\n * announced to assistive tech). Every string is overridable per-instance via a\n * matching prop, so consumers can localise or reword without forking.\n */\nexport const useDevPanelLabels = createTranslation({\n copy: { no: \"Kopier\", se: \"Kopiera\", fi: \"Kopioi\", es: \"Copiar\" },\n copied: { no: \"Kopiert\", se: \"Kopierad\", fi: \"Kopioitu\", es: \"Copiado\" },\n copyJson: {\n no: \"Kopier JSON\",\n se: \"Kopiera JSON\",\n fi: \"Kopioi JSON\",\n es: \"Copiar JSON\",\n },\n close: { no: \"Lukk\", se: \"Stäng\", fi: \"Sulje\", es: \"Cerrar\" },\n minimise: {\n no: \"Minimer\",\n se: \"Minimera\",\n fi: \"Pienennä\",\n es: \"Minimizar\",\n },\n expand: { no: \"Utvid\", se: \"Expandera\", fi: \"Laajenna\", es: \"Expandir\" },\n hide: { no: \"Skjul\", se: \"Dölj\", fi: \"Piilota\", es: \"Ocultar\" },\n show: { no: \"Vis\", se: \"Visa\", fi: \"Näytä\", es: \"Mostrar\" },\n step: { no: \"Steg\", se: \"Steg\", fi: \"Vaihe\", es: \"Paso\" },\n statusCompleted: {\n no: \"fullført\",\n se: \"slutförd\",\n fi: \"valmis\",\n es: \"completado\",\n },\n statusCurrent: {\n no: \"gjeldende\",\n se: \"aktuell\",\n fi: \"nykyinen\",\n es: \"actual\",\n },\n statusUpcoming: {\n no: \"ikke startet\",\n se: \"inte påbörjad\",\n fi: \"aloittamatta\",\n es: \"no iniciado\",\n },\n needsAttention: {\n no: \"krever oppmerksomhet\",\n se: \"kräver uppmärksamhet\",\n fi: \"vaatii huomiota\",\n es: \"requiere atención\",\n },\n});\n","\"use client\";\n\nimport { EyeOff, Minus, Plus } from \"lucide-react\";\nimport {\n createContext,\n useContext,\n useEffect,\n useId,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n type RefObject,\n} from \"react\";\n\nimport { IconButton } from \"../index\";\nimport styles from \"./dev-panel.module.css\";\nimport { useDevPanelLabels } from \"./use-dev-panel-labels\";\n\nconst join = (...parts: (string | undefined)[]) =>\n parts.filter(Boolean).join(\" \");\n\n/**\n * Exposes the current section's heading `id` so an interactive group nested in\n * a `DevPanel.Section` (e.g. a `radiogroup`) can wire `aria-labelledby` to a\n * real, co-located element — the id can never dangle.\n */\nconst DevPanelSectionContext = createContext<string | undefined>(undefined);\n\n/** Reads the enclosing `DevPanel.Section` heading id, if any. */\nexport function useDevPanelSectionLabelId() {\n return useContext(DevPanelSectionContext);\n}\n\n/** What `DevPanel.Handle` needs from the panel it brings back. */\ntype DevPanelRootContextValue = {\n show: () => void;\n title: ReactNode;\n handleRef: RefObject<HTMLButtonElement | null>;\n};\n\nconst DevPanelRootContext = createContext<DevPanelRootContextValue | null>(\n null,\n);\n\n// Below the `sm` breakpoint (600px), where the panel would cover the form.\nconst NARROW_SCREEN = \"(max-width: 599.95px)\";\n\nconst isMac = () =>\n typeof navigator !== \"undefined\" &&\n /mac|iphone|ipad/i.test(navigator.platform || navigator.userAgent);\n\n/**\n * Whether a key press is the shortcut, written as `\"Mod+Shift+D\"`: the key\n * last, after any of `Mod` (⌘ on a Mac, Ctrl elsewhere), `Ctrl`, `Meta`,\n * `Alt` and `Shift`. A modifier not named must not be held.\n */\nexport function matchesShortcut(\n event: KeyboardEvent,\n shortcut: string,\n): boolean {\n const parts = shortcut.split(\"+\").map((part) => part.trim().toLowerCase());\n const key = parts.pop();\n if (!key) return false;\n\n const mod = parts.includes(\"mod\");\n const wants = {\n ctrl: parts.includes(\"ctrl\") || (mod && !isMac()),\n meta: parts.includes(\"meta\") || (mod && isMac()),\n alt: parts.includes(\"alt\"),\n shift: parts.includes(\"shift\"),\n };\n if (\n event.ctrlKey !== wants.ctrl ||\n event.metaKey !== wants.meta ||\n event.altKey !== wants.alt ||\n event.shiftKey !== wants.shift\n )\n return false;\n\n // By the physical key as well, as Alt and Shift change `key` (\"∂\", \"D\").\n return (\n event.key.toLowerCase() === key ||\n event.code.toLowerCase() === `key${key}` ||\n event.code.toLowerCase() === `digit${key}`\n );\n}\n\nexport type DevPanelProps = {\n /** Panel title shown in the header. Defaults to `\"Dev controls\"`. */\n title?: ReactNode;\n /** Optional pill beside the title, e.g. `\"MOCK\"` or `\"live\"`. */\n badge?: ReactNode;\n /** Show the small status dot before the title. Defaults to `true`. */\n showStatusDot?: boolean;\n /** Render the minimise control. Defaults to `true`. */\n minimisable?: boolean;\n /** Controlled minimised state. Omit for uncontrolled. */\n minimised?: boolean;\n /** Initial minimised state when uncontrolled. Defaults to `false`. */\n defaultMinimised?: boolean;\n /** Called whenever the minimised state changes. */\n onMinimisedChange?: (minimised: boolean) => void;\n /** Accessible name for the minimise button. Defaults to a translated label. */\n minimiseLabel?: string;\n /** Accessible name for the restore button. Defaults to a translated label. */\n expandLabel?: string;\n /**\n * Lets the panel be hidden completely: a hide button joins the minimise\n * button, `shortcut` toggles it, and while it is hidden `handle` (a small\n * tab at the edge of the window) brings it back. On a narrow screen the\n * panel also starts minimised, drawn as a small round button. Defaults to\n * `true` when `hidden` or `defaultHidden` is passed.\n */\n hideable?: boolean;\n /** Controlled hidden state. Omit for uncontrolled. */\n hidden?: boolean;\n /** Initial hidden state when uncontrolled. Defaults to `false`. */\n defaultHidden?: boolean;\n /** Called whenever the hidden state changes. Store it to keep it across reloads. */\n onHiddenChange?: (hidden: boolean) => void;\n /**\n * The keys that hide and show the panel, anywhere on the page. `Mod` is ⌘\n * on a Mac and Ctrl elsewhere. `false` turns it off.\n * @default \"Mod+Shift+D\"\n */\n shortcut?: string | false;\n /** What shows while the panel is hidden. Defaults to `<DevPanel.Handle />`. */\n handle?: ReactNode;\n /** Accessible name for the hide button. Defaults to a translated label. */\n hideLabel?: string;\n /** Section content: compose the `DevPanel.*` sub-components here. */\n children?: ReactNode;\n /** Class on the outermost element, for placing the panel. */\n className?: string;\n /** Inline style on the outermost element, for placing the panel. */\n style?: CSSProperties;\n};\n\n/**\n * The root of the reusable dev panel: a self-contained, content-only card that\n * form apps compose from the `DevPanel.*` sub-components. It owns the chrome\n * (title, optional badge, minimise control) and layout; every section is fully\n * controlled by the consumer via `value` + `onChange`/callback props.\n *\n * It renders only the panel — placement (fixed corner, drawer, …) is left to\n * the consuming app.\n *\n * ```tsx\n * <DevPanel.Root badge=\"MOCK\">\n * <DevPanel.Country value={country} onChange={setCountry} options={countries} />\n * <DevPanel.Status value={status} onChange={setStatus} options={statuses} />\n * <DevPanel.Payload value={payload} />\n * <DevPanel.Footer onReseed={reseed} onCopyUrl={() => {}} url={url} />\n * </DevPanel.Root>\n * ```\n */\nexport function DevPanel({\n title = \"Dev controls\",\n badge,\n showStatusDot = true,\n minimisable = true,\n minimised,\n defaultMinimised,\n onMinimisedChange,\n minimiseLabel,\n expandLabel,\n hidden,\n defaultHidden,\n onHiddenChange,\n hideable = hidden !== undefined || defaultHidden !== undefined,\n shortcut = \"Mod+Shift+D\",\n handle,\n hideLabel,\n children,\n className,\n style,\n}: DevPanelProps) {\n const t = useDevPanelLabels();\n const isControlled = minimised !== undefined;\n const [internalMinimised, setInternalMinimised] = useState(\n defaultMinimised ?? false,\n );\n const isMinimised = isControlled ? minimised : internalMinimised;\n\n const [internalHidden, setInternalHidden] = useState(defaultHidden ?? false);\n const isHidden = hideable && (hidden ?? internalHidden);\n\n const rootRef = useRef<HTMLDivElement>(null);\n const handleRef = useRef<HTMLButtonElement>(null);\n const hideButtonRef = useRef<HTMLButtonElement>(null);\n // Where focus goes once the panel or the handle has rendered.\n const pendingFocus = useRef<\"handle\" | \"panel\" | null>(null);\n\n const toggleMinimised = () => {\n const next = !isMinimised;\n if (!isControlled) setInternalMinimised(next);\n onMinimisedChange?.(next);\n };\n\n const setHiddenState = (next: boolean) => {\n if (hidden === undefined) setInternalHidden(next);\n onHiddenChange?.(next);\n };\n\n // A hideable panel on a narrow screen starts minimised, unless told otherwise.\n useEffect(() => {\n if (\n hideable &&\n !isControlled &&\n defaultMinimised === undefined &&\n typeof window !== \"undefined\" &&\n window.matchMedia?.(NARROW_SCREEN).matches\n )\n setInternalMinimised(true);\n // Once, as a default: not again when the window is resized.\n }, []);\n\n useEffect(() => {\n const target = pendingFocus.current;\n pendingFocus.current = null;\n if (target === \"handle\") handleRef.current?.focus();\n if (target === \"panel\") hideButtonRef.current?.focus();\n });\n\n // The latest state for the listener, which is added once.\n const latest = useRef({ isHidden, setHiddenState });\n latest.current = { isHidden, setHiddenState };\n\n useEffect(() => {\n if (!hideable || shortcut === false) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.repeat || !matchesShortcut(event, shortcut)) return;\n event.preventDefault();\n const { isHidden: wasHidden, setHiddenState: set } = latest.current;\n const active = document.activeElement;\n // Focus follows only when it was in what goes away.\n if (!wasHidden && rootRef.current?.contains(active))\n pendingFocus.current = \"handle\";\n if (wasHidden && active != null && active === handleRef.current)\n pendingFocus.current = \"panel\";\n set(!wasHidden);\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, [hideable, shortcut]);\n\n const bodyId = useId();\n\n if (isHidden) {\n return (\n <DevPanelRootContext.Provider\n value={{\n show: () => {\n pendingFocus.current = \"panel\";\n setHiddenState(false);\n },\n title,\n handleRef,\n }}\n >\n {handle ?? <DevPanelHandle />}\n </DevPanelRootContext.Provider>\n );\n }\n\n return (\n <div\n ref={rootRef}\n data-dev-panel-root=\"\"\n data-hideable={hideable ? \"\" : undefined}\n data-minimised={isMinimised ? \"\" : undefined}\n className={join(styles.DevPanel, className)}\n style={style}\n >\n <header className={styles.Header}>\n <span className={styles.HeaderTitle}>\n {showStatusDot && <span className={styles.HeaderDot} aria-hidden />}\n <span>{title}</span>\n {badge != null && <span className={styles.Badge}>{badge}</span>}\n </span>\n <span className={styles.HeaderActions}>\n {hideable && (\n <IconButton\n ref={hideButtonRef}\n className={styles.HideButton}\n aria-label={hideLabel ?? t.hide}\n onClick={() => {\n pendingFocus.current = \"handle\";\n setHiddenState(true);\n }}\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n >\n <EyeOff size={18} aria-hidden />\n </IconButton>\n )}\n {minimisable && (\n <IconButton\n aria-label={\n isMinimised\n ? (expandLabel ?? t.expand)\n : (minimiseLabel ?? t.minimise)\n }\n aria-expanded={!isMinimised}\n aria-controls={bodyId}\n onClick={toggleMinimised}\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n >\n {isMinimised ? (\n <Plus size={18} aria-hidden />\n ) : (\n <Minus size={18} aria-hidden />\n )}\n </IconButton>\n )}\n </span>\n </header>\n {!isMinimised && (\n <div id={bodyId} className={styles.Body}>\n {children}\n </div>\n )}\n </div>\n );\n}\n\nexport type DevPanelSectionProps = {\n /** The uppercase section heading (e.g. `\"Country\"`). */\n label: ReactNode;\n /** Optional trailing meta shown on the heading row (e.g. a `6 / 10` count). */\n meta?: ReactNode;\n children?: ReactNode;\n /** Class on the `section` element. */\n className?: string;\n /** Inline style on the `section` element. */\n style?: CSSProperties;\n};\n\n/**\n * A labelled section wrapper. Renders the uppercase heading and exposes its\n * generated id through context so a nested interactive group can reference it\n * via `aria-labelledby`. Used internally by every `DevPanel.*` section;\n * exported so consumers can build bespoke sections with the same look and\n * wiring.\n */\nexport function DevPanelSection({\n label,\n meta,\n children,\n className,\n style,\n}: DevPanelSectionProps) {\n const labelId = useId();\n\n return (\n <section\n aria-labelledby={labelId}\n className={join(styles.Section, className)}\n style={style}\n >\n <div className={styles.SectionHeaderRow}>\n <span id={labelId} className={styles.SectionLabel}>\n {label}\n </span>\n {meta != null && <span className={styles.SectionMeta}>{meta}</span>}\n </div>\n <DevPanelSectionContext.Provider value={labelId}>\n {children}\n </DevPanelSectionContext.Provider>\n </section>\n );\n}\n\nexport type DevPanelHandleProps = {\n /** The edge of the window the tab sits on. Defaults to `\"right\"`. */\n side?: \"left\" | \"right\";\n /** The text on the tab. Defaults to the panel's `title`. */\n label?: ReactNode;\n /** Class on the button, for placing it differently. */\n className?: string;\n /** Inline style on the button, for placing it differently. */\n style?: CSSProperties;\n};\n\n/**\n * The tab that stands in for a hidden `DevPanel.Root` and brings it back: a\n * small button fixed to the bottom of the right (or left) edge of the window.\n * The panel renders it while hidden, so it is only used as the panel's\n * `handle`, to change its side or text:\n *\n * ```tsx\n * <DevPanel.Root hideable handle={<DevPanel.Handle side=\"left\" />}>\n * ```\n */\nexport function DevPanelHandle({\n side = \"right\",\n label,\n className,\n style,\n}: DevPanelHandleProps) {\n const t = useDevPanelLabels();\n const root = useContext(DevPanelRootContext);\n if (!root) return null;\n\n const text = label ?? root.title;\n\n return (\n <button\n ref={root.handleRef}\n type=\"button\"\n className={join(styles.Handle, className)}\n style={style}\n data-side={side}\n // The visible text, with what pressing it does in front.\n aria-label={typeof text === \"string\" ? `${t.show} ${text}` : undefined}\n onClick={root.show}\n >\n <span className={styles.HeaderDot} aria-hidden />\n <span>{text}</span>\n </button>\n );\n}\n","\"use client\";\n\nimport {\n useCallback,\n useRef,\n type KeyboardEvent,\n type RefCallback,\n} from \"react\";\n\ntype RovingRadioItemProps = {\n // A callback, so it fits whichever element the option is drawn as.\n ref: RefCallback<HTMLElement>;\n role: \"radio\";\n \"aria-checked\": boolean;\n \"aria-disabled\": true | undefined;\n tabIndex: 0 | -1;\n onClick: () => void;\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;\n};\n\ntype UseRovingRadioOptions<T> = {\n /** All selectable values, in DOM order. */\n values: readonly T[];\n /** The currently selected value (or `undefined` when nothing is selected). */\n selected: T | undefined;\n /** Called with the value to select on click / keyboard activation. */\n onSelect: (value: T) => void;\n /** Optional per-value disabled predicate; disabled items are skipped. */\n isDisabled?: (value: T, index: number) => boolean;\n};\n\n/**\n * Wires the WAI-ARIA radiogroup keyboard pattern for a set of custom option\n * elements: a single roving `tabIndex`, arrow / Home / End navigation that moves\n * focus *and* selection (activation follows focus), and Space / Enter to\n * (re)select the focused item. Disabled items are skipped.\n *\n * The consuming component owns the markup and styling; it just spreads\n * `getItemProps(index)` onto each option and renders the group with\n * `role=\"radiogroup\"`.\n */\nexport function useRovingRadio<T>({\n values,\n selected,\n onSelect,\n isDisabled,\n}: UseRovingRadioOptions<T>) {\n const refs = useRef<(HTMLElement | null)[]>([]);\n\n const disabledAt = useCallback(\n (index: number) => isDisabled?.(values[index], index) ?? false,\n [isDisabled, values],\n );\n\n const selectedIndex = values.findIndex((value) => value === selected);\n const firstEnabledIndex = values.findIndex((_, index) => !disabledAt(index));\n // Exactly one item is tabbable: the selected one, or the first enabled option\n // when there is no selection yet.\n const tabbableIndex =\n selectedIndex >= 0 && !disabledAt(selectedIndex)\n ? selectedIndex\n : firstEnabledIndex;\n\n const focusAndSelect = useCallback(\n (index: number) => {\n refs.current[index]?.focus();\n onSelect(values[index]);\n },\n [onSelect, values],\n );\n\n const nextEnabled = useCallback(\n (from: number, direction: 1 | -1) => {\n const count = values.length;\n for (let step = 1; step <= count; step += 1) {\n const index = (from + direction * step + count * step) % count;\n if (!disabledAt(index)) return index;\n }\n return from;\n },\n [disabledAt, values.length],\n );\n\n const getItemProps = useCallback(\n (index: number): RovingRadioItemProps => {\n const value = values[index];\n const disabled = disabledAt(index);\n\n return {\n ref: (element: HTMLElement | null) => {\n refs.current[index] = element;\n },\n role: \"radio\",\n \"aria-checked\": value === selected,\n \"aria-disabled\": disabled || undefined,\n tabIndex: index === tabbableIndex ? 0 : -1,\n onClick: () => {\n if (!disabled) onSelect(value);\n },\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => {\n switch (event.key) {\n case \"ArrowRight\":\n case \"ArrowDown\":\n event.preventDefault();\n focusAndSelect(nextEnabled(index, 1));\n break;\n case \"ArrowLeft\":\n case \"ArrowUp\":\n event.preventDefault();\n focusAndSelect(nextEnabled(index, -1));\n break;\n case \"Home\":\n event.preventDefault();\n focusAndSelect(nextEnabled(-1, 1));\n break;\n case \"End\":\n event.preventDefault();\n focusAndSelect(nextEnabled(0, -1));\n break;\n case \" \":\n case \"Enter\":\n event.preventDefault();\n if (!disabled) onSelect(value);\n break;\n default:\n break;\n }\n },\n };\n },\n [\n disabledAt,\n focusAndSelect,\n nextEnabled,\n onSelect,\n selected,\n tabbableIndex,\n values,\n ],\n );\n\n return { getItemProps };\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { useDevPanelSectionLabelId } from \"./dev-panel\";\nimport styles from \"./dev-panel.module.css\";\nimport { useRovingRadio } from \"./use-roving-radio\";\n\n/** A single option in a segmented single-select. */\nexport type SegmentedOption<T extends string> = {\n /** The value reported to `onChange` when this option is selected. */\n value: T;\n /** Visible label. */\n label: ReactNode;\n /** Optional leading icon (mark decorative icons `aria-hidden`). */\n icon?: ReactNode;\n disabled?: boolean;\n};\n\n/**\n * Requires an accessible name for the `radiogroup`. When rendered inside a\n * `DevPanel.Section` the group is labelled by the section heading\n * automatically, so a name is only required when used standalone.\n */\ntype SegmentedAccessibleName =\n | { \"aria-label\"?: string; \"aria-labelledby\"?: string };\n\nexport type SegmentedRadioGroupProps<T extends string> = {\n /** The selected value (fully controlled). */\n value: T;\n /** Called with the newly selected value. */\n onChange: (value: T) => void;\n options: SegmentedOption<T>[];\n /**\n * `\"pill\"` (default) — options fill the beige track, selected flips to a\n * white pill. `\"chip\"` — options hug their content and carry a small leading\n * dot.\n */\n variant?: \"pill\" | \"chip\";\n} & SegmentedAccessibleName;\n\n/**\n * An accessible single-select segmented control built on the roving-radiogroup\n * pattern. Internal to `DevPanel`; Country / Variant / Step state compose it.\n */\nexport function SegmentedRadioGroup<T extends string>({\n value,\n onChange,\n options,\n variant = \"pill\",\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n}: SegmentedRadioGroupProps<T>) {\n const sectionLabelId = useDevPanelSectionLabelId();\n const labelledBy = ariaLabelledby ?? (ariaLabel ? undefined : sectionLabelId);\n\n const { getItemProps } = useRovingRadio<T>({\n values: options.map((option) => option.value),\n selected: value,\n onSelect: onChange,\n isDisabled: (_, index) => options[index]?.disabled ?? false,\n });\n\n const isChip = variant === \"chip\";\n\n return (\n <div\n role=\"radiogroup\"\n aria-label={ariaLabel}\n aria-labelledby={labelledBy}\n className={isChip ? styles.ChipGroup : styles.SegmentedGroup}\n >\n {options.map((option, index) => {\n // The chosen option is styled from `aria-checked`, which is in here.\n const { onKeyDown, ...itemProps } = getItemProps(index);\n return (\n <button\n key={option.value}\n type=\"button\"\n data-disabled={option.disabled ? \"\" : undefined}\n onKeyDown={onKeyDown}\n {...itemProps}\n className={isChip ? styles.Chip : styles.SegmentedOption}\n >\n {isChip ? (\n <span className={styles.ChipDot} aria-hidden />\n ) : (\n option.icon\n )}\n <span>{option.label}</span>\n </button>\n );\n })}\n </div>\n );\n}\n","import type { ReactNode } from \"react\";\n\nimport { DevPanelSection } from \"./dev-panel\";\nimport styles from \"./dev-panel.module.css\";\nimport {\n SegmentedRadioGroup,\n type SegmentedOption,\n} from \"./segmented-control\";\n\n/** A selectable country. Provide between 2 and 4. */\nexport type DevPanelCountryOption<T extends string> = {\n /** The value reported to `onChange` (e.g. an ISO code). */\n value: T;\n /** Country name shown in the pill. */\n label: ReactNode;\n /** Optional short code shown, muted, before the name (e.g. `\"SE\"`). */\n code?: ReactNode;\n disabled?: boolean;\n};\n\nexport type DevPanelCountryProps<T extends string> = {\n /** The selected country (fully controlled). */\n value: T;\n /** Called with the newly selected country. */\n onChange: (value: T) => void;\n /** 2-4 country options. */\n options: DevPanelCountryOption<T>[];\n /** Section heading. Defaults to `\"Country\"`. */\n label?: ReactNode;\n};\n\n/**\n * Segmented country selector. Renders each option as a pill with an optional\n * muted code before the name. Single-select and fully controlled.\n */\nexport function DevPanelCountry<T extends string>({\n value,\n onChange,\n options,\n label = \"Country\",\n}: DevPanelCountryProps<T>) {\n const segmentedOptions: SegmentedOption<T>[] = options.map((option) => ({\n value: option.value,\n disabled: option.disabled,\n label: option.label,\n icon:\n option.code != null ? (\n <span className={styles.CountryCode}>{option.code}</span>\n ) : undefined,\n }));\n\n return (\n <DevPanelSection label={label}>\n <SegmentedRadioGroup\n value={value}\n onChange={onChange}\n options={segmentedOptions}\n />\n </DevPanelSection>\n );\n}\n","import type { ReactNode } from \"react\";\n\nimport { DevPanelSection } from \"./dev-panel\";\nimport {\n SegmentedRadioGroup,\n type SegmentedOption,\n} from \"./segmented-control\";\n\n/** A selectable flow variant. */\nexport type DevPanelVariantOption<T extends string> = {\n value: T;\n label: ReactNode;\n disabled?: boolean;\n};\n\nexport type DevPanelVariantProps<T extends string> = {\n /** The selected variant (fully controlled). */\n value: T;\n /** Called with the newly selected variant. */\n onChange: (value: T) => void;\n options: DevPanelVariantOption<T>[];\n /** Section heading. Defaults to `\"Variant\"`. */\n label?: ReactNode;\n};\n\n/**\n * Segmented flow-variant selector (e.g. cash loan / refinance / top-up).\n * Single-select and fully controlled.\n */\nexport function DevPanelVariant<T extends string>({\n value,\n onChange,\n options,\n label = \"Variant\",\n}: DevPanelVariantProps<T>) {\n const segmentedOptions: SegmentedOption<T>[] = options.map((option) => ({\n value: option.value,\n label: option.label,\n disabled: option.disabled,\n }));\n\n return (\n <DevPanelSection label={label}>\n <SegmentedRadioGroup\n value={value}\n onChange={onChange}\n options={segmentedOptions}\n />\n </DevPanelSection>\n );\n}\n","import type { ReactNode } from \"react\";\n\nimport { Text } from \"../index\";\nimport { DevPanelSection } from \"./dev-panel\";\nimport styles from \"./dev-panel.module.css\";\nimport {\n SegmentedRadioGroup,\n type SegmentedOption,\n} from \"./segmented-control\";\n\n/** A selectable variation of the current step. */\nexport type DevPanelStepStateOption<T extends string> = {\n value: T;\n label: ReactNode;\n disabled?: boolean;\n};\n\nexport type DevPanelStepStateProps<T extends string> = {\n /** The selected step-state variation (fully controlled). */\n value: T;\n /** Called with the newly selected variation. */\n onChange: (value: T) => void;\n options: DevPanelStepStateOption<T>[];\n /** Section heading. Defaults to `\"Step state\"`. */\n label?: ReactNode;\n /**\n * Name of the step the variations belong to (e.g. `\"Skulder\"`). When set, a\n * `\"{variationsLabel} {stepLabel}\"` line is shown above the chips.\n */\n stepLabel?: ReactNode;\n /** Prefix for the step-label line. Defaults to `\"Variations for\"`. */\n variationsLabel?: ReactNode;\n};\n\n/**\n * Segmented chips for the different states/variations of the current step (e.g.\n * required debts / voluntary / no debts). Single-select and fully controlled.\n */\nexport function DevPanelStepState<T extends string>({\n value,\n onChange,\n options,\n label = \"Step state\",\n stepLabel,\n variationsLabel = \"Variations for\",\n}: DevPanelStepStateProps<T>) {\n const segmentedOptions: SegmentedOption<T>[] = options.map((option) => ({\n value: option.value,\n label: option.label,\n disabled: option.disabled,\n }));\n\n return (\n <DevPanelSection label={label}>\n {stepLabel != null && (\n <Text\n textStyle=\"body-sm\"\n style={{\n color: \"var(--lea-color-neutral-600)\",\n marginBlockEnd: \"var(--lea-spacing-0-5)\",\n }}\n >\n {variationsLabel}{\" \"}\n <span className={styles.StepStateName}>{stepLabel}</span>\n </Text>\n )}\n <SegmentedRadioGroup\n value={value}\n onChange={onChange}\n options={segmentedOptions}\n variant=\"chip\"\n />\n </DevPanelSection>\n );\n}\n","\"use client\";\n\nimport { Check } from \"lucide-react\";\nimport type { CSSProperties, ReactNode } from \"react\";\n\nimport { useDevPanelSectionLabelId, DevPanelSection } from \"./dev-panel\";\nimport styles from \"./dev-panel.module.css\";\nimport { useDevPanelLabels } from \"./use-dev-panel-labels\";\n\ntype StepCircleState = \"complete\" | \"current\" | \"upcoming\";\n\n/** A single step in the jump-to-step grid. */\nexport type DevPanelStep = {\n /** Short label shown beneath the circle (e.g. `\"Skulder\"`). */\n label: ReactNode;\n /**\n * Explicit visual state. When omitted it is derived from `currentStep`\n * (before → `complete`, at → `current`, after → `upcoming`).\n */\n status?: StepCircleState;\n /** Marks the step with a warning dot (e.g. it needs attention). */\n flagged?: boolean;\n disabled?: boolean;\n};\n\nexport type DevPanelStepsProps = {\n /** The ordered steps. */\n steps: DevPanelStep[];\n /**\n * The active step (1-based). Drives the count and, for any step without an\n * explicit `status`, the derived complete/current/upcoming state.\n */\n currentStep?: number;\n /** Called when a step is chosen, with its 1-based number and the step. */\n onStepSelect?: (step: number, item: DevPanelStep) => void;\n /** Section heading. Defaults to `\"Jump to step\"`. */\n label?: ReactNode;\n /** Show the `currentStep / total` count in the heading. Defaults to `true`. */\n showCount?: boolean;\n /** Number of step circles per row. Defaults to `4`. */\n columns?: number;\n};\n\nconst statusWord = (\n state: StepCircleState,\n t: ReturnType<typeof useDevPanelLabels>,\n) => {\n switch (state) {\n case \"complete\":\n return t.statusCompleted;\n case \"current\":\n return t.statusCurrent;\n default:\n return t.statusUpcoming;\n }\n};\n\n/**\n * A grid of numbered step circles for jumping between form steps. Completed\n * steps show a check, the current step is filled, and a warning dot flags steps\n * that need attention. Fully controlled: the consumer supplies the steps and the\n * active step, and handles selection.\n */\nexport function DevPanelSteps({\n steps,\n currentStep,\n onStepSelect,\n label = \"Jump to step\",\n showCount = true,\n columns = 4,\n}: DevPanelStepsProps) {\n const t = useDevPanelLabels();\n const total = steps.length;\n const meta =\n showCount && currentStep != null ? `${currentStep} / ${total}` : undefined;\n\n return (\n <DevPanelSection label={label} meta={meta}>\n <StepGrid\n steps={steps}\n currentStep={currentStep}\n onStepSelect={onStepSelect}\n columns={columns}\n t={t}\n />\n </DevPanelSection>\n );\n}\n\nfunction StepGrid({\n steps,\n currentStep,\n onStepSelect,\n columns,\n t,\n}: {\n steps: DevPanelStep[];\n currentStep?: number;\n onStepSelect?: (step: number, item: DevPanelStep) => void;\n columns: number;\n t: ReturnType<typeof useDevPanelLabels>;\n}) {\n const groupLabelId = useDevPanelSectionLabelId();\n\n return (\n <div\n role=\"group\"\n aria-labelledby={groupLabelId}\n className={styles.StepGrid}\n style={{ \"--_columns\": columns } as CSSProperties}\n >\n {steps.map((item, index) => {\n const stepNumber = index + 1;\n const state: StepCircleState =\n item.status ??\n (currentStep == null\n ? \"upcoming\"\n : stepNumber < currentStep\n ? \"complete\"\n : stepNumber === currentStep\n ? \"current\"\n : \"upcoming\");\n const isCurrent = state === \"current\";\n const isComplete = state === \"complete\";\n\n // The button is named by what it shows, the number and the label,\n // with the words a sighted user reads from the drawing added for\n // assistive tech: \"Step\" before the number, and the status in\n // brackets after the label, as in \"Steg 6 Skulder (gjeldende)\". An\n // `aria-label` would replace the visible text instead of adding to\n // it, and could not hold a label that is not a string.\n return (\n <button\n key={stepNumber}\n type=\"button\"\n aria-current={isCurrent ? \"step\" : undefined}\n data-state={state}\n data-disabled={item.disabled ? \"\" : undefined}\n disabled={item.disabled}\n onClick={() => onStepSelect?.(stepNumber, item)}\n className={styles.StepButton}\n >\n <span className={styles.StepCircle}>\n <span className={styles.VisuallyHidden}>\n {/* A completed step shows a check in place of its number. */}\n {isComplete ? `${t.step} ${stepNumber}` : `${t.step} `}\n </span>\n {isComplete ? <Check size={18} aria-hidden /> : stepNumber}\n {item.flagged && (\n <span className={styles.StepFlagDot} aria-hidden />\n )}\n </span>{\" \"}\n <span className={styles.StepLabel}>{item.label}</span>\n <span className={styles.VisuallyHidden}>\n {/* Brackets, not a comma: browsers put a space before this\n part of the name, which would leave the comma on its own. */}\n {`(${statusWord(state, t)}${\n item.flagged ? `, ${t.needsAttention}` : \"\"\n })`}\n </span>\n </button>\n );\n })}\n </div>\n );\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { useDevPanelSectionLabelId, DevPanelSection } from \"./dev-panel\";\nimport styles from \"./dev-panel.module.css\";\nimport { useRovingRadio } from \"./use-roving-radio\";\n\n/** The statuses a card can be coloured as. `neutral` is a status with no colour of its own. */\nexport type DevPanelStatusColor =\n | \"warning\"\n | \"success\"\n | \"info\"\n | \"error\"\n | \"neutral\";\n\n/** A selectable application status. */\nexport type DevPanelStatusOption<T extends string> = {\n value: T;\n label: ReactNode;\n /**\n * Colour of the leading dot / selected border. Defaults to `\"neutral\"`. The\n * dot is decorative — status is always conveyed by the label text too, so it\n * never relies on colour alone.\n */\n color?: DevPanelStatusColor;\n disabled?: boolean;\n};\n\nexport type DevPanelStatusProps<T extends string> = {\n /** The selected status (fully controlled). */\n value: T;\n /** Called with the newly selected status. */\n onChange: (value: T) => void;\n options: DevPanelStatusOption<T>[];\n /** Section heading. Defaults to `\"Application status\"`. */\n label?: ReactNode;\n};\n\n/**\n * A radio grid for the application status. Each card carries a decorative\n * coloured dot mapped to a status token; the status is always spelled out in the\n * label so it is never signalled by colour alone. Single-select and fully\n * controlled.\n */\nexport function DevPanelStatus<T extends string>({\n value,\n onChange,\n options,\n label = \"Application status\",\n}: DevPanelStatusProps<T>) {\n return (\n <DevPanelSection label={label}>\n <StatusGrid value={value} onChange={onChange} options={options} />\n </DevPanelSection>\n );\n}\n\nfunction StatusGrid<T extends string>({\n value,\n onChange,\n options,\n}: {\n value: T;\n onChange: (value: T) => void;\n options: DevPanelStatusOption<T>[];\n}) {\n const groupLabelId = useDevPanelSectionLabelId();\n const { getItemProps } = useRovingRadio<T>({\n values: options.map((option) => option.value),\n selected: value,\n onSelect: onChange,\n isDisabled: (_, index) => options[index]?.disabled ?? false,\n });\n\n return (\n <div\n role=\"radiogroup\"\n aria-labelledby={groupLabelId}\n className={styles.StatusGrid}\n >\n {options.map((option, index) => {\n // The chosen card is styled from `aria-checked`, which is in here.\n const { onKeyDown, ...itemProps } = getItemProps(index);\n return (\n <button\n key={option.value}\n type=\"button\"\n data-color={option.color ?? \"neutral\"}\n data-disabled={option.disabled ? \"\" : undefined}\n onKeyDown={onKeyDown}\n {...itemProps}\n className={styles.StatusCard}\n >\n <span className={styles.StatusDot} aria-hidden />\n <span>{option.label}</span>\n </button>\n );\n })}\n </div>\n );\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\n/**\n * Writes text to the clipboard and tracks a short-lived `copied` flag so the\n * caller can announce success (via a `role=\"status\"` live region) and flip an\n * icon. The flag auto-resets after ~2s; the pending timer is cleared on unmount.\n */\nexport function useCopy() {\n const [copied, setCopied] = useState(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(\n undefined,\n );\n\n const copy = useCallback(async (text: string) => {\n try {\n await navigator.clipboard?.writeText(text);\n } catch {\n // Clipboard can reject (permissions, insecure context). Still surface the\n // confirmation so the interaction feels responsive; the dev can retry.\n }\n setCopied(true);\n clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => setCopied(false), 2000);\n }, []);\n\n useEffect(() => () => clearTimeout(timeoutRef.current), []);\n\n return { copied, copy };\n}\n","\"use client\";\n\nimport { Check, Copy } from \"lucide-react\";\nimport { Fragment, type ReactNode } from \"react\";\n\nimport { IconButton } from \"../index\";\nimport styles from \"./dev-panel.module.css\";\nimport { useCopy } from \"./use-copy\";\nimport { useDevPanelLabels } from \"./use-dev-panel-labels\";\n\nexport type DevPanelFlowProps = {\n /** Flow identifier (e.g. a short hash). */\n flowId?: ReactNode;\n /** Process name (e.g. `\"loan-se\"`). */\n process?: ReactNode;\n /** Variant name (e.g. `\"refinance\"`). */\n variant?: ReactNode;\n /** Leading label. Defaults to `\"Flow\"`. */\n label?: ReactNode;\n /** Show the copy button. Defaults to `true`. */\n showCopy?: boolean;\n /**\n * String written to the clipboard. Defaults to the string parts joined by\n * `\" · \"`.\n */\n copyValue?: string;\n /** Accessible name for the copy button. Defaults to a translated label. */\n copyLabel?: string;\n /** Called after a successful copy. */\n onCopy?: () => void;\n};\n\nconst asCopyString = (part: ReactNode) =>\n typeof part === \"string\" || typeof part === \"number\" ? String(part) : null;\n\n/**\n * The flow header row: an uppercase label followed by the `id · process ·\n * variant` breadcrumb and a copy button. Copy success is announced via a\n * `role=\"status\"` live region.\n */\nexport function DevPanelFlow({\n flowId,\n process,\n variant,\n label = \"Flow\",\n showCopy = true,\n copyValue,\n copyLabel,\n onCopy,\n}: DevPanelFlowProps) {\n const t = useDevPanelLabels();\n const { copied, copy } = useCopy();\n\n const parts = [flowId, process, variant].filter(\n (part): part is ReactNode => part != null,\n );\n const derivedCopy = parts\n .map(asCopyString)\n .filter((part): part is string => part != null)\n .join(\" · \");\n const textToCopy = copyValue ?? derivedCopy;\n\n const handleCopy = async () => {\n await copy(textToCopy);\n onCopy?.();\n };\n\n return (\n <div className={styles.Flow}>\n <div className={styles.FlowText}>\n <span className={styles.FlowLabel}>{label}</span>\n <span>\n {parts.map((part, index) => (\n <Fragment key={index}>\n {index > 0 && (\n <span aria-hidden className={styles.FlowSeparator}>\n ·\n </span>\n )}\n {part}\n </Fragment>\n ))}\n </span>\n </div>\n {showCopy && (\n <IconButton\n aria-label={copyLabel ?? t.copy}\n onClick={handleCopy}\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n >\n {copied ? (\n <Check size={16} aria-hidden />\n ) : (\n <Copy size={16} aria-hidden />\n )}\n </IconButton>\n )}\n <span role=\"status\" aria-live=\"polite\" className={styles.VisuallyHidden}>\n {copied ? t.copied : \"\"}\n </span>\n </div>\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\n\nimport styles from \"./dev-panel.module.css\";\n\n/** The kinds of token the JSON highlighter distinguishes. */\ntype JsonTokenType =\n | \"key\"\n | \"string\"\n | \"number\"\n | \"boolean\"\n | \"null\"\n | \"punctuation\";\n\ntype Token = { type: JsonTokenType; value: string };\n\n// Matches the value-bearing tokens (strings, numbers, and the JSON keywords).\n// Everything between matches (braces, brackets, commas, colons, whitespace) is\n// treated as punctuation.\nconst TOKEN =\n /\"(?:\\\\.|[^\"\\\\])*\"|-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?|\\b(?:true|false|null)\\b/g;\n\nconst isSpace = (char: string) =>\n char === \" \" || char === \"\\t\" || char === \"\\n\" || char === \"\\r\";\n\nfunction classify(raw: string, code: string, end: number): JsonTokenType {\n if (raw[0] === '\"') {\n // A string is a key when the next non-space character is a colon.\n let i = end;\n while (i < code.length && isSpace(code[i])) i += 1;\n return code[i] === \":\" ? \"key\" : \"string\";\n }\n if (raw === \"true\" || raw === \"false\") return \"boolean\";\n if (raw === \"null\") return \"null\";\n return \"number\";\n}\n\nfunction tokenize(code: string): Token[] {\n const tokens: Token[] = [];\n let lastIndex = 0;\n\n for (const match of code.matchAll(TOKEN)) {\n const index = match.index ?? 0;\n if (index > lastIndex) {\n tokens.push({ type: \"punctuation\", value: code.slice(lastIndex, index) });\n }\n const raw = match[0];\n tokens.push({ type: classify(raw, code, index + raw.length), value: raw });\n lastIndex = index + raw.length;\n }\n\n if (lastIndex < code.length) {\n tokens.push({ type: \"punctuation\", value: code.slice(lastIndex) });\n }\n\n return tokens;\n}\n\n/**\n * Returns `true` when `value` should be syntax-highlighted as JSON: any non-null\n * non-string value (objects, arrays, numbers, booleans), or a string that parses\n * cleanly as JSON. Plain prose strings return `false` so they render verbatim.\n */\nexport function looksLikeJson(value: unknown): boolean {\n if (typeof value !== \"string\") return value != null;\n const trimmed = value.trim();\n if (!trimmed) return false;\n try {\n JSON.parse(trimmed);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Renders a serialised JSON string as coloured spans. Output is built from React\n * nodes (never `dangerouslySetInnerHTML`), so a string value such as `\"<img>\"`\n * is shown as text and can never inject markup. Each span carries its kind as\n * `data-token`, and the stylesheet has one colour for each kind.\n */\nexport function JsonHighlight({ code }: { code: string }) {\n const tokens = useMemo(() => tokenize(code), [code]);\n\n return (\n <>\n {tokens.map((token, index) => (\n <span key={index} className={styles.Token} data-token={token.type}>\n {token.value}\n </span>\n ))}\n </>\n );\n}\n","\"use client\";\n\nimport { Braces, Check, ChevronRight, Copy, X } from \"lucide-react\";\nimport {\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { IconButton } from \"../index\";\nimport styles from \"./dev-panel.module.css\";\nimport { JsonHighlight, looksLikeJson } from \"./json-highlight\";\nimport { useCopy } from \"./use-copy\";\nimport { useDevPanelLabels } from \"./use-dev-panel-labels\";\n\n/** Re-points the button's colour properties for the dark payload panel. */\nconst onDarkButtonStyle = {\n \"--button-fg\": \"var(--lea-palette-text-contrast)\",\n \"--button-border\": \"var(--lea-palette-border-contrast)\",\n \"--button-tint\": \"var(--lea-color-neutral-800)\",\n} as CSSProperties;\n\nexport type DevPanelPayloadProps = {\n /**\n * The payload to preview. Objects are pretty-printed as JSON; strings are\n * shown verbatim.\n */\n value: unknown;\n /** Label on the toggle. Defaults to `\"Payload preview\"`. */\n label?: ReactNode;\n /** Heading of the expanded dark panel. Defaults to `label`. */\n title?: ReactNode;\n /**\n * Where the payload opens. `\"inline\"` (default) expands a dark panel below the\n * toggle; `\"side\"` opens a floating panel beside the dev panel as a dialog\n * (dismissed with Escape or a click outside).\n */\n placement?: \"inline\" | \"side\";\n /** Optional pill in the side flyout header, e.g. `\"live\"`. Ignored inline. */\n badge?: ReactNode;\n /** Controlled open state. Omit for uncontrolled. */\n open?: boolean;\n /** Initial open state when uncontrolled. Defaults to `false`. */\n defaultOpen?: boolean;\n /** Called whenever the open state changes. */\n onOpenChange?: (open: boolean) => void;\n /** Accessible name for the copy button. Defaults to a translated label. */\n copyLabel?: string;\n /** Visible label for the side flyout's copy action. Defaults to a translated label. */\n copyJsonLabel?: string;\n /** Accessible name for the side flyout's close button. Defaults to a translated label. */\n closeLabel?: string;\n /**\n * Syntax-highlight JSON payloads. Defaults to `true`. Non-JSON strings always\n * render verbatim. Set `false` for the plain single-colour view.\n */\n highlight?: boolean;\n};\n\nconst serialise = (value: unknown): string => {\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n};\n\n// Avoid the useLayoutEffect SSR warning while still positioning before paint on\n// the client (the flyout only opens in response to a client interaction).\nconst useIsomorphicLayoutEffect =\n typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\ntype FlyoutRect = { top: number; left: number; width: number; height: number };\n\nconst GAP = 12;\nconst MAX_WIDTH = 420;\nconst MIN_WIDTH = 280;\n\n/**\n * A payload preview. The toggle is a disclosure (`aria-expanded`/`aria-controls`)\n * that reveals the pretty-printed JSON with a copy control; copy success is\n * announced via a `role=\"status\"` live region. With `placement=\"inline\"`\n * (default) it expands a dark panel below the toggle; with `placement=\"side\"` it\n * opens a floating dialog beside the panel (Escape / click-outside to close,\n * focus moves in and returns to the toggle on close). Uncontrolled by default;\n * drive it with `open`/`onOpenChange` when needed.\n */\nexport function DevPanelPayload({\n value,\n label = \"Payload preview\",\n title,\n placement = \"inline\",\n badge,\n open,\n defaultOpen = false,\n onOpenChange,\n copyLabel,\n copyJsonLabel,\n closeLabel,\n highlight = true,\n}: DevPanelPayloadProps) {\n const t = useDevPanelLabels();\n const { copied, copy } = useCopy();\n const isControlled = open !== undefined;\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const isOpen = isControlled ? open : internalOpen;\n const panelId = useId();\n const titleId = useId();\n const isSide = placement === \"side\";\n\n const toggleRef = useRef<HTMLButtonElement>(null);\n const flyoutRef = useRef<HTMLDivElement>(null);\n const [rect, setRect] = useState<FlyoutRect | null>(null);\n // The side flyout is rendered into the body, which is only there in the\n // browser, so the server and the first client render leave it out.\n const [portalTarget, setPortalTarget] = useState<HTMLElement | null>(null);\n useEffect(() => setPortalTarget(document.body), []);\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange],\n );\n\n const text = serialise(value);\n const body =\n highlight && looksLikeJson(value) ? <JsonHighlight code={text} /> : text;\n\n const position = useCallback(() => {\n const anchor =\n (toggleRef.current?.closest(\n \"[data-dev-panel-root]\",\n ) as HTMLElement | null) ?? toggleRef.current;\n if (!anchor) return;\n const r = anchor.getBoundingClientRect();\n const width = Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, r.width));\n // Prefer the left of the panel; flip to the right when there isn't room.\n let left = r.left - width - GAP;\n if (left < GAP) {\n left = Math.min(r.right + GAP, window.innerWidth - width - GAP);\n }\n const height = Math.min(r.height, window.innerHeight - GAP * 2);\n const top = Math.min(\n Math.max(GAP, r.top),\n window.innerHeight - GAP - height,\n );\n setRect({ top, left, width, height });\n }, []);\n\n useIsomorphicLayoutEffect(() => {\n if (!isSide || !isOpen) return;\n position();\n const onChange = () => position();\n window.addEventListener(\"resize\", onChange);\n window.addEventListener(\"scroll\", onChange, true);\n return () => {\n window.removeEventListener(\"resize\", onChange);\n window.removeEventListener(\"scroll\", onChange, true);\n };\n }, [isSide, isOpen, position]);\n\n // Until the flyout has a place it is hidden, and a hidden element cannot\n // take focus, so the focus handling waits for the first `rect`.\n const isPlaced = rect !== null;\n\n useEffect(() => {\n if (!isSide || !isOpen || !portalTarget || !isPlaced) return;\n const flyout = flyoutRef.current;\n const toggle = toggleRef.current;\n\n const focusable = () =>\n Array.from(\n flyout?.querySelectorAll<HTMLElement>(\n 'button, [href], input, textarea, select, [tabindex]:not([tabindex=\"-1\"])',\n ) ?? [],\n ).filter((el) => !el.hasAttribute(\"disabled\"));\n\n (focusable()[0] ?? flyout)?.focus();\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n setOpen(false);\n return;\n }\n if (event.key !== \"Tab\") return;\n const items = focusable();\n if (items.length === 0) {\n event.preventDefault();\n flyout?.focus();\n return;\n }\n const first = items[0];\n const last = items[items.length - 1];\n const active = document.activeElement;\n if (event.shiftKey && (active === first || active === flyout)) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n }\n };\n\n const onPointerDown = (event: PointerEvent) => {\n const target = event.target as Node;\n if (flyout?.contains(target) || toggle?.contains(target)) return;\n setOpen(false);\n };\n\n document.addEventListener(\"keydown\", onKeyDown, true);\n document.addEventListener(\"pointerdown\", onPointerDown, true);\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown, true);\n document.removeEventListener(\"pointerdown\", onPointerDown, true);\n toggle?.focus();\n };\n }, [isSide, isOpen, portalTarget, isPlaced, setOpen]);\n\n const copyButton = (\n <IconButton\n aria-label={copyLabel ?? t.copy}\n onClick={() => copy(text)}\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n className={styles.OnDark}\n style={onDarkButtonStyle}\n >\n {copied ? <Check size={16} aria-hidden /> : <Copy size={16} aria-hidden />}\n </IconButton>\n );\n\n return (\n <div>\n <button\n ref={toggleRef}\n type=\"button\"\n aria-expanded={isOpen}\n aria-controls={panelId}\n data-placement={placement}\n onClick={() => setOpen(!isOpen)}\n className={styles.PayloadToggle}\n >\n <span className={styles.PayloadToggleLabel}>\n <Braces size={16} aria-hidden />\n {label}\n </span>\n <span className={styles.PayloadChevron}>\n <ChevronRight size={16} aria-hidden />\n </span>\n </button>\n\n {!isSide && isOpen && (\n <div id={panelId} className={styles.PayloadPanel}>\n <div className={styles.PayloadHeader}>\n <span>{title ?? label}</span>\n {copyButton}\n </div>\n <pre tabIndex={0} className={styles.PayloadCode}>\n {body}\n </pre>\n </div>\n )}\n\n {isSide &&\n isOpen &&\n portalTarget &&\n createPortal(\n <div\n ref={flyoutRef}\n id={panelId}\n role=\"dialog\"\n aria-labelledby={titleId}\n tabIndex={-1}\n className={styles.Flyout}\n style={\n rect\n ? {\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n }\n : { visibility: \"hidden\" }\n }\n >\n <div className={styles.PayloadHeader}>\n <span className={styles.FlyoutTitle}>\n <span id={titleId}>{title ?? label}</span>\n {badge != null && (\n <span className={styles.Badge} data-on-dark=\"\">\n {badge}\n </span>\n )}\n </span>\n <IconButton\n aria-label={closeLabel ?? t.close}\n onClick={() => setOpen(false)}\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n className={styles.OnDark}\n style={onDarkButtonStyle}\n >\n <X size={16} aria-hidden />\n </IconButton>\n </div>\n <pre tabIndex={0} className={styles.PayloadCode}>\n {body}\n </pre>\n <div className={styles.FlyoutFooter}>\n <button\n type=\"button\"\n onClick={() => copy(text)}\n className={styles.FlyoutCopyButton}\n >\n {copied ? (\n <Check size={16} aria-hidden />\n ) : (\n <Copy size={16} aria-hidden />\n )}\n {copied ? t.copied : (copyJsonLabel ?? t.copyJson)}\n </button>\n </div>\n </div>,\n portalTarget,\n )}\n\n <span role=\"status\" aria-live=\"polite\" className={styles.VisuallyHidden}>\n {copied ? t.copied : \"\"}\n </span>\n </div>\n );\n}\n","\"use client\";\n\nimport { ChevronRight, RotateCcw, Upload } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { Button } from \"../index\";\nimport styles from \"./dev-panel.module.css\";\nimport { useCopy } from \"./use-copy\";\n\nexport type DevPanelFooterProps = {\n /** Called when \"Reseed\" is pressed. Omit to hide the button. */\n onReseed?: () => void;\n /** Label for the reseed button. Defaults to `\"Reseed\"`. */\n reseedLabel?: ReactNode;\n /** Called when \"Copy URL\" is pressed. Omit to hide the button. */\n onCopyUrl?: () => void;\n /** When set, this URL is written to the clipboard before `onCopyUrl` fires. */\n url?: string;\n /** Label for the copy-URL button. Defaults to `\"Copy URL\"`. */\n copyUrlLabel?: ReactNode;\n /** Called when \"Console\" is pressed. Omit to hide the button. */\n onConsole?: () => void;\n /** Label for the console button. Defaults to `\"Console\"`. */\n consoleLabel?: ReactNode;\n /** Extra actions appended after the built-in buttons. */\n children?: ReactNode;\n};\n\n/**\n * The footer action row. Renders a button only for each callback provided, so\n * apps opt into just the actions they support. `Copy URL` writes `url` to the\n * clipboard (when given) before invoking `onCopyUrl`. Extra `children` are\n * appended after the built-ins.\n */\nexport function DevPanelFooter({\n onReseed,\n reseedLabel = \"Reseed\",\n onCopyUrl,\n url,\n copyUrlLabel = \"Copy URL\",\n onConsole,\n consoleLabel = \"Console\",\n children,\n}: DevPanelFooterProps) {\n const { copy } = useCopy();\n\n const handleCopyUrl = async () => {\n if (url != null) await copy(url);\n onCopyUrl?.();\n };\n\n return (\n <div className={styles.Footer}>\n {onReseed && (\n <Button\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n startIcon={<RotateCcw aria-hidden />}\n onClick={onReseed}\n >\n {reseedLabel}\n </Button>\n )}\n {onCopyUrl && (\n <Button\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n startIcon={<Upload aria-hidden />}\n onClick={handleCopyUrl}\n >\n {copyUrlLabel}\n </Button>\n )}\n {onConsole && (\n <Button\n variant=\"solid\"\n size=\"sm\"\n endIcon={<ChevronRight aria-hidden />}\n onClick={onConsole}\n style={{ marginInlineStart: \"auto\" }}\n >\n {consoleLabel}\n </Button>\n )}\n {children}\n </div>\n );\n}\n","/*\n * The members of the `DevPanel` namespace. The parts live in files of their\n * own under their full names (`DevPanelStatus` in dev-panel-status.tsx);\n * here each gets the short name it has in the namespace (`DevPanel.Status`).\n */\nexport {\n DevPanel as Root,\n DevPanelHandle as Handle,\n DevPanelSection as Section,\n useDevPanelSectionLabelId as useSectionLabelId,\n type DevPanelHandleProps as HandleProps,\n type DevPanelProps as RootProps,\n type DevPanelSectionProps as SectionProps,\n} from \"./dev-panel\";\nexport {\n DevPanelCountry as Country,\n type DevPanelCountryOption as CountryOption,\n type DevPanelCountryProps as CountryProps,\n} from \"./dev-panel-country\";\nexport {\n DevPanelVariant as Variant,\n type DevPanelVariantOption as VariantOption,\n type DevPanelVariantProps as VariantProps,\n} from \"./dev-panel-variant\";\nexport {\n DevPanelStepState as StepState,\n type DevPanelStepStateOption as StepStateOption,\n type DevPanelStepStateProps as StepStateProps,\n} from \"./dev-panel-step-state\";\nexport {\n DevPanelSteps as Steps,\n type DevPanelStep as Step,\n type DevPanelStepsProps as StepsProps,\n} from \"./dev-panel-steps\";\nexport {\n DevPanelStatus as Status,\n type DevPanelStatusColor as StatusColor,\n type DevPanelStatusOption as StatusOption,\n type DevPanelStatusProps as StatusProps,\n} from \"./dev-panel-status\";\nexport {\n DevPanelFlow as Flow,\n type DevPanelFlowProps as FlowProps,\n} from \"./dev-panel-flow\";\nexport {\n DevPanelPayload as Payload,\n type DevPanelPayloadProps as PayloadProps,\n} from \"./dev-panel-payload\";\nexport {\n DevPanelFooter as Footer,\n type DevPanelFooterProps as FooterProps,\n} from \"./dev-panel-footer\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dev-panel.mjs","names":[],"sources":["../src/dev-panel/dev-panel.module.css","../src/dev-panel/use-dev-panel-labels.ts","../src/dev-panel/dev-panel.tsx","../src/dev-panel/use-roving-radio.ts","../src/dev-panel/segmented-control.tsx","../src/dev-panel/dev-panel-country.tsx","../src/dev-panel/dev-panel-variant.tsx","../src/dev-panel/dev-panel-step-state.tsx","../src/dev-panel/dev-panel-steps.tsx","../src/dev-panel/dev-panel-status.tsx","../src/dev-panel/use-copy.ts","../src/dev-panel/dev-panel-flow.tsx","../src/dev-panel/json-highlight.tsx","../src/dev-panel/dev-panel-payload.tsx","../src/dev-panel/dev-panel-footer.tsx","../src/dev-panel/parts.ts"],"sourcesContent":["/*\n * Design tokens come from src/theme/tokens.css, generated from src/theme/tokens\n * by `pnpm tokens:css`. It is a plain stylesheet rather than runtime-injected\n * CSS variables, so the panel renders correctly without a provider.\n *\n * One stylesheet for every part of the dev panel. The panel is a tool for the\n * people building a form app and has a look of its own: it is denser than the\n * components in src/components/, so several of its distances sit between two\n * steps of the spacing scale (0.25, 0.75, 1.25 and 1.75 steps), and its small\n * labels are uppercase. Neither is a precedent for a component.\n *\n * A state is read from the attribute that announces it (`aria-checked`,\n * `aria-expanded`) where there is one, so the look cannot disagree with what\n * assistive tech is told.\n */\n\n/*\n * The small text of the panel is 12px, one pixel below the smallest step of\n * the type scale (--lea-font-step-minus-2, 13px). At 13px a long step label\n * (\"Civilinformation\") no longer fits its cell in the grid of four and breaks\n * one letter from its end.\n */\n.Badge,\n.SectionLabel,\n.SectionMeta,\n.FlowLabel,\n.CountryCode,\n.StepLabel,\n.PayloadCode,\n.PayloadEditor,\n.PayloadError {\n font-size: 0.75rem;\n}\n\n/* ----------------------------------------------------------------------------\n * The panel\n * ------------------------------------------------------------------------- */\n\n/* The outer card: a rounded, cream surface with a subtle border. */\n.DevPanel {\n display: flex;\n overflow: hidden;\n width: 100%;\n /* 460px, the width the panel is drawn at. It follows a narrower container. */\n max-width: 28.75rem;\n flex-direction: column;\n border: 1px solid var(--lea-palette-border-subtle);\n border-radius: var(--lea-radius-4xl);\n background-color: var(--lea-color-beige-50);\n color: var(--lea-palette-text-base);\n font-family: inherit;\n}\n\n/*\n * A panel with a `position`, fixed to a corner of the window over the app, so\n * a second panel can sit on the other side. It floats, so it has the shadow of\n * the payload flyout, and it is never taller than the window: the body scrolls.\n * 1300 is the layer of the handle and the flyout.\n */\n.DevPanel[data-position] {\n position: fixed;\n z-index: 1300;\n bottom: var(--lea-spacing-2);\n width: min(28.75rem, calc(100% - var(--lea-spacing-4)));\n max-height: calc(100dvh - var(--lea-spacing-4));\n box-shadow: var(--lea-shadow-2xl);\n}\n\n.DevPanel[data-position=\"bottom-right\"] {\n inset-inline-end: var(--lea-spacing-2);\n}\n\n.DevPanel[data-position=\"bottom-left\"] {\n inset-inline-start: var(--lea-spacing-2);\n}\n\n.DevPanel[data-position] .Body {\n min-height: 0;\n}\n\n/*\n * A pinned panel, docked to its side of the window at full height from `md`,\n * where the app has room to move aside for it. Narrower, it floats as above.\n */\n@media (min-width: 900px) {\n .DevPanel[data-position][data-pinned] {\n top: 0;\n bottom: 0;\n max-height: none;\n border-block: 0;\n border-radius: 0;\n box-shadow: none;\n }\n\n .DevPanel[data-position=\"bottom-right\"][data-pinned] {\n inset-inline-end: 0;\n border-inline-end: 0;\n }\n\n .DevPanel[data-position=\"bottom-left\"][data-pinned] {\n inset-inline-start: 0;\n border-inline-start: 0;\n }\n}\n\n@media (max-width: 899.95px) {\n .PinButton {\n display: none;\n }\n}\n\n/* The chrome strip: title and status dot, an optional badge, the minimise control. */\n.Header {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n padding-block: calc(var(--lea-spacing) * 1.75);\n padding-inline: var(--lea-spacing-2-5);\n border-bottom: 1px solid var(--lea-palette-border-subtle);\n background-color: var(--lea-color-beige-100);\n gap: var(--lea-spacing-1-5);\n}\n\n.HeaderTitle {\n display: inline-flex;\n align-items: center;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-font-step-0);\n font-weight: var(--lea-font-weight-semibold);\n gap: var(--lea-spacing-1);\n}\n\n/* The pin, hide and minimise buttons, side by side. */\n.HeaderActions {\n display: inline-flex;\n align-items: center;\n gap: var(--lea-spacing-1);\n}\n\n/* The small \"live\" indicator dot beside the panel title. */\n.HeaderDot {\n width: var(--lea-spacing-1);\n height: var(--lea-spacing-1);\n flex-shrink: 0;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-tertiary-base);\n}\n\n/* The \"MOCK\" / \"live\" pill next to a title. */\n.Badge {\n display: inline-flex;\n align-items: center;\n padding-block: calc(var(--lea-spacing) * 0.25);\n padding-inline: calc(var(--lea-spacing) * 0.75);\n border-radius: var(--lea-radius-sm);\n background-color: var(--lea-color-neutral-200);\n color: var(--lea-color-neutral-700);\n font-weight: var(--lea-font-weight-semibold);\n letter-spacing: 0.08em;\n line-height: var(--lea-line-height-snug);\n text-transform: uppercase;\n}\n\n/* The same pill in the header of the dark side flyout. */\n.Badge[data-on-dark] {\n background-color: var(--lea-color-neutral-800);\n color: var(--lea-color-neutral-300);\n}\n\n/*\n * The tab that brings a hidden panel back (`DevPanel.Handle`): the header of\n * the panel, cut down to its dot and title, fixed to the bottom of the edge of\n * the window. It floats over the app, so it has the shadow the panel's own\n * flyout has, a size smaller. 1300 is the layer of the option lists, above an\n * app's own content.\n */\n.Handle {\n position: fixed;\n z-index: 1300;\n bottom: var(--lea-spacing-2);\n display: inline-flex;\n align-items: center;\n padding-block: var(--lea-spacing-1);\n padding-inline: var(--lea-spacing-1-5);\n border: 1px solid var(--lea-palette-border-subtle);\n margin: 0;\n background-color: var(--lea-color-beige-100);\n box-shadow: var(--lea-shadow-md);\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--lea-text-label-md-font-size);\n /* Semibold, as the title in the header it stands for. */\n font-weight: var(--lea-font-weight-semibold);\n gap: var(--lea-spacing-1);\n letter-spacing: var(--lea-text-label-md-letter-spacing);\n line-height: var(--lea-text-label-md-line-height);\n}\n\n/* Flush with the edge: no border and no rounding on that side. */\n.Handle[data-side=\"right\"] {\n right: 0;\n border-inline-end: 0;\n border-radius: var(--lea-radius-xl) 0 0 var(--lea-radius-xl);\n}\n\n.Handle[data-side=\"left\"] {\n left: 0;\n border-inline-start: 0;\n border-radius: 0 var(--lea-radius-xl) var(--lea-radius-xl) 0;\n}\n\n@media (hover: hover) {\n .Handle:hover {\n background-color: var(--lea-color-beige-200);\n }\n}\n\n.Handle:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n outline-offset: 0.125rem;\n}\n\n/*\n * A hideable panel, minimised, on a narrow screen: a small round button and\n * nothing more, so it does not cover the form. The title stays for screen\n * readers; the hide button goes, as there is nothing left to hide.\n */\n@media (max-width: 599.95px) {\n .DevPanel[data-hideable][data-minimised] {\n width: auto;\n border-radius: var(--lea-radius-full);\n box-shadow: var(--lea-shadow-md);\n }\n\n .DevPanel[data-hideable][data-minimised] .Header {\n padding: var(--lea-spacing-0-5);\n border-bottom: 0;\n }\n\n .DevPanel[data-hideable][data-minimised] .HeaderTitle {\n position: absolute;\n overflow: hidden;\n width: 1px;\n height: 1px;\n clip-path: inset(50%);\n white-space: nowrap;\n }\n\n .DevPanel[data-hideable][data-minimised] .HideButton {\n display: none;\n }\n}\n\n/* The body that holds the sections. It scrolls when the app limits the height. */\n.Body {\n display: flex;\n flex-direction: column;\n padding: var(--lea-spacing-2-5);\n gap: var(--lea-spacing-2-5);\n overflow-y: auto;\n}\n\n/* ----------------------------------------------------------------------------\n * A labelled section (COUNTRY, VARIANT, …)\n * ------------------------------------------------------------------------- */\n\n.Section {\n display: flex;\n flex-direction: column;\n gap: calc(var(--lea-spacing) * 1.25);\n}\n\n.SectionHeaderRow {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: var(--lea-spacing-1);\n}\n\n/*\n * The uppercase section heading. neutral-600 rather than text-subtle, so the\n * small label clears 4.5:1 against the cream body, as the `Tabs` track does.\n */\n.SectionLabel {\n margin: 0;\n color: var(--lea-color-neutral-600);\n font-weight: var(--lea-font-weight-semibold);\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n/* Optional trailing meta on a section heading row (e.g. the `6 / 10` count). */\n.SectionMeta {\n color: var(--lea-color-neutral-600);\n font-weight: var(--lea-font-weight-medium);\n}\n\n/* ----------------------------------------------------------------------------\n * The flow row\n * ------------------------------------------------------------------------- */\n\n.Flow {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n padding-block: var(--lea-spacing-1);\n padding-inline: var(--lea-spacing-1-5);\n border-radius: var(--lea-radius-lg);\n background-color: var(--lea-color-beige-100);\n gap: var(--lea-spacing-1);\n}\n\n.FlowText {\n display: inline-flex;\n min-width: 0;\n align-items: center;\n color: var(--lea-palette-text-base);\n font-size: var(--lea-font-step-minus-1);\n gap: calc(var(--lea-spacing) * 0.75);\n}\n\n/* The label and the breadcrumb each stay on one line and end in an ellipsis. */\n.FlowText > span {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.FlowLabel {\n flex-shrink: 0;\n color: var(--lea-color-neutral-600);\n font-weight: var(--lea-font-weight-semibold);\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n/* The dot between two parts of the breadcrumb. */\n.FlowSeparator {\n margin-inline: var(--lea-spacing-0-5);\n color: var(--lea-palette-text-muted);\n}\n\n/* ----------------------------------------------------------------------------\n * Segmented single-select (Country, Variant, Step state)\n * ------------------------------------------------------------------------- */\n\n/*\n * The beige track that holds the options. It scrolls sideways when they do\n * not fit, with the scrollbar hidden so the row stays a clean pill.\n */\n.SegmentedGroup {\n display: flex;\n max-width: 100%;\n flex-flow: row nowrap;\n align-items: stretch;\n padding: var(--lea-spacing-0-5);\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-bg-muted);\n gap: var(--lea-spacing-0-5);\n overflow-x: auto;\n scrollbar-width: none;\n}\n\n.SegmentedGroup::-webkit-scrollbar {\n display: none;\n}\n\n/*\n * The \"pill\" variant used by Country and Variant: the options share the track\n * evenly and the chosen one flips to a white pill.\n */\n.SegmentedOption {\n position: relative;\n display: inline-flex;\n min-width: 0;\n flex: 1 1 0;\n align-items: center;\n justify-content: center;\n padding-block: var(--lea-spacing-1);\n padding-inline: var(--lea-spacing-1-5);\n border: none;\n border-radius: var(--lea-radius-full);\n margin: 0;\n background-color: transparent;\n box-shadow: none;\n color: var(--lea-color-neutral-600);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-medium);\n gap: calc(var(--lea-spacing) * 0.75);\n line-height: var(--lea-line-height-none);\n transition:\n background-color var(--lea-duration-normal) var(--lea-easing-standard),\n color var(--lea-duration-normal) var(--lea-easing-standard),\n box-shadow var(--lea-duration-normal) var(--lea-easing-standard);\n user-select: none;\n white-space: nowrap;\n}\n\n.SegmentedOption[aria-checked=\"true\"] {\n background-color: var(--lea-palette-bg-base);\n /* A hairline of shade under the pill, in the beige of the track. */\n box-shadow: 0 1px 2px var(--lea-color-beige-300);\n color: var(--lea-palette-text-base);\n font-weight: var(--lea-font-weight-semibold);\n}\n\n.SegmentedOption svg {\n display: block;\n flex-shrink: 0;\n}\n\n/* The short code before a country name (e.g. `SE`). */\n.CountryCode {\n color: var(--lea-color-neutral-600);\n font-weight: var(--lea-font-weight-semibold);\n}\n\n/*\n * The \"chip\" variant used by Step state: pills that hug their content, wrap\n * onto more lines and carry a small leading dot.\n */\n.ChipGroup {\n display: flex;\n flex-flow: row wrap;\n gap: var(--lea-spacing-1);\n}\n\n.Chip {\n display: inline-flex;\n align-items: center;\n padding-block: calc(var(--lea-spacing) * 0.75);\n padding-inline: var(--lea-spacing-1-5);\n border: 1px solid var(--lea-palette-border-base);\n border-radius: var(--lea-radius-full);\n margin: 0;\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-medium);\n gap: calc(var(--lea-spacing) * 0.75);\n /* Between `none` and `tight`: the padding sets the height of the pill. */\n line-height: 1.2;\n transition:\n background-color var(--lea-duration-normal) var(--lea-easing-standard),\n color var(--lea-duration-normal) var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard);\n user-select: none;\n white-space: nowrap;\n}\n\n.Chip[aria-checked=\"true\"] {\n border-color: var(--lea-palette-border-accent);\n background-color: var(--lea-palette-primary-subtle);\n color: var(--lea-palette-text-accent);\n font-weight: var(--lea-font-weight-semibold);\n}\n\n/* 7px, off the spacing scale like the distances above. */\n.ChipDot {\n width: calc(var(--lea-spacing) * 0.875);\n height: calc(var(--lea-spacing) * 0.875);\n flex-shrink: 0;\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-color-neutral-400);\n}\n\n.Chip[aria-checked=\"true\"] .ChipDot {\n background-color: var(--lea-palette-primary-base);\n}\n\n.SegmentedOption[data-disabled],\n.Chip[data-disabled] {\n cursor: not-allowed;\n opacity: 0.45;\n pointer-events: none;\n}\n\n/* ----------------------------------------------------------------------------\n * Jump-to-step grid\n * ------------------------------------------------------------------------- */\n\n/* The number of columns arrives inline from the `columns` prop. */\n.StepGrid {\n display: grid;\n gap: var(--lea-spacing-1-5);\n grid-template-columns: repeat(var(--_columns), minmax(0, 1fr));\n}\n\n.StepButton {\n display: flex;\n /* Lets the button shrink to its grid track, so a long label wraps instead of\n running into the next cell. */\n min-width: 0;\n flex-direction: column;\n align-items: center;\n padding: 0;\n border: none;\n border-radius: var(--lea-radius-lg);\n margin: 0;\n background: none;\n cursor: pointer;\n font-family: inherit;\n gap: calc(var(--lea-spacing) * 0.75);\n}\n\n.StepButton[data-disabled] {\n cursor: not-allowed;\n opacity: 0.5;\n pointer-events: none;\n}\n\n/* An upcoming step. The other two states set the three properties below. */\n.StepCircle {\n --step-bg: transparent;\n --step-border: var(--lea-color-neutral-400);\n --step-fg: var(--lea-color-neutral-600);\n\n position: relative;\n display: inline-flex;\n width: var(--lea-spacing-5);\n height: var(--lea-spacing-5);\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n border: 2px solid var(--step-border);\n border-radius: var(--lea-radius-full);\n background-color: var(--step-bg);\n color: var(--step-fg);\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-semibold);\n line-height: var(--lea-line-height-none);\n transition:\n background-color var(--lea-duration-normal) var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard),\n color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.StepButton[data-state=\"complete\"] .StepCircle {\n --step-bg: var(--lea-palette-tertiary-base);\n --step-border: var(--lea-palette-tertiary-base);\n --step-fg: var(--lea-palette-tertiary-contrast);\n}\n\n.StepButton[data-state=\"current\"] .StepCircle {\n --step-bg: var(--lea-palette-primary-base);\n --step-border: var(--lea-palette-primary-base);\n --step-fg: var(--lea-palette-primary-contrast);\n}\n\n.StepCircle svg {\n display: block;\n}\n\n/* The small warning dot on a step that needs attention, ringed in the body colour. */\n.StepFlagDot {\n position: absolute;\n inset-block-start: -1px;\n inset-inline-end: -1px;\n width: calc(var(--lea-spacing) * 1.25);\n height: calc(var(--lea-spacing) * 1.25);\n border: 2px solid var(--lea-color-beige-50);\n border-radius: var(--lea-radius-full);\n background-color: var(--lea-palette-status-warning-base);\n}\n\n.StepLabel {\n /* As wide as the cell, and a long word breaks, so labels never overlap. */\n width: 100%;\n color: var(--lea-color-neutral-600);\n font-weight: var(--lea-font-weight-regular);\n line-height: 1.2;\n overflow-wrap: anywhere;\n text-align: center;\n}\n\n.StepButton[data-state=\"current\"] .StepLabel {\n color: var(--lea-palette-text-base);\n font-weight: var(--lea-font-weight-semibold);\n}\n\n/* ----------------------------------------------------------------------------\n * Step state\n * ------------------------------------------------------------------------- */\n\n/* The name of the step in the \"Variations for …\" line. */\n.StepStateName {\n color: var(--lea-palette-text-base);\n font-weight: var(--lea-font-weight-semibold);\n}\n\n/* ----------------------------------------------------------------------------\n * Application status cards\n * ------------------------------------------------------------------------- */\n\n.StatusGrid {\n display: grid;\n gap: calc(var(--lea-spacing) * 1.25);\n grid-template-columns: repeat(2, minmax(0, 1fr));\n}\n\n/* `neutral` is the base rule; each status sets the two properties below. */\n.StatusCard {\n --status-base: var(--lea-color-neutral-400);\n --status-subtle: var(--lea-color-neutral-100);\n\n display: inline-flex;\n width: 100%;\n align-items: center;\n padding-block: calc(var(--lea-spacing) * 1.25);\n padding-inline: var(--lea-spacing-1-5);\n border: 1px solid var(--lea-palette-border-base);\n border-radius: var(--lea-radius-lg);\n margin: 0;\n background-color: var(--lea-palette-bg-base);\n color: var(--lea-palette-text-base);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-medium);\n gap: var(--lea-spacing-1);\n line-height: 1.2;\n text-align: start;\n transition:\n background-color var(--lea-duration-normal) var(--lea-easing-standard),\n border-color var(--lea-duration-normal) var(--lea-easing-standard);\n}\n\n.StatusCard[data-color=\"warning\"] {\n --status-base: var(--lea-palette-status-warning-base);\n --status-subtle: var(--lea-palette-status-warning-subtle);\n}\n\n.StatusCard[data-color=\"success\"] {\n --status-base: var(--lea-palette-status-success-base);\n --status-subtle: var(--lea-palette-status-success-subtle);\n}\n\n.StatusCard[data-color=\"info\"] {\n --status-base: var(--lea-palette-status-info-base);\n --status-subtle: var(--lea-palette-status-info-subtle);\n}\n\n.StatusCard[data-color=\"error\"] {\n --status-base: var(--lea-palette-status-error-base);\n --status-subtle: var(--lea-palette-status-error-subtle);\n}\n\n.StatusCard[aria-checked=\"true\"] {\n border-color: var(--status-base);\n background-color: var(--status-subtle);\n font-weight: var(--lea-font-weight-semibold);\n}\n\n.StatusCard[data-disabled] {\n cursor: not-allowed;\n opacity: 0.5;\n pointer-events: none;\n}\n\n/* Decorative: the status is always spelled out in the label beside it. */\n.StatusDot {\n width: calc(var(--lea-spacing) * 1.25);\n height: calc(var(--lea-spacing) * 1.25);\n flex-shrink: 0;\n border-radius: var(--lea-radius-full);\n background-color: var(--status-base);\n}\n\n/* ----------------------------------------------------------------------------\n * Payload preview\n * ------------------------------------------------------------------------- */\n\n.PayloadToggle {\n display: flex;\n width: 100%;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n padding-block: calc(var(--lea-spacing) * 1.25);\n padding-inline: var(--lea-spacing-1-5);\n border: none;\n border-radius: var(--lea-radius-lg);\n margin: 0;\n background-color: var(--lea-palette-bg-accent);\n color: var(--lea-palette-text-accent);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-semibold);\n gap: var(--lea-spacing-1);\n}\n\n.PayloadToggleLabel {\n display: inline-flex;\n align-items: center;\n gap: var(--lea-spacing-1);\n}\n\n/* Points right while closed and down while the panel below is open. */\n.PayloadChevron {\n display: inline-flex;\n transform: rotate(0deg);\n transition: transform var(--lea-duration-emphasis) var(--lea-easing-standard);\n}\n\n.PayloadToggle[aria-expanded=\"true\"] .PayloadChevron {\n transform: rotate(90deg);\n}\n\n/*\n * With `placement=\"side\"` it points left, to where the flyout appears beside\n * the panel, open or closed: `aria-expanded` carries the state.\n */\n.PayloadToggle[data-placement=\"side\"] .PayloadChevron {\n transform: rotate(180deg);\n transition: none;\n}\n\n.PayloadChevron svg {\n display: block;\n}\n\n/* The dark panel that opens below the toggle. */\n.PayloadPanel {\n overflow: hidden;\n border-radius: var(--lea-radius-lg);\n margin-block-start: calc(var(--lea-spacing) * 1.25);\n background-color: var(--lea-color-neutral-950);\n}\n\n/* With `placement=\"panel\"` there is no toggle above it. */\n.PayloadPanel[data-placement=\"panel\"] {\n margin-block-start: 0;\n}\n\n/*\n * In a panel with a `position`, which is never taller than the window, the\n * payload takes the height the other sections leave and scrolls inside it.\n * It keeps 20 steps, so beside many sections the body scrolls instead.\n */\n.DevPanel[data-position] .Payload[data-placement=\"panel\"],\n.DevPanel[data-position] .PayloadPanel[data-placement=\"panel\"] {\n display: flex;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n}\n\n.DevPanel[data-position] .PayloadPanel[data-placement=\"panel\"] .PayloadCode,\n.DevPanel[data-position] .PayloadPanel[data-placement=\"panel\"] .PayloadEditor {\n min-height: calc(var(--lea-spacing) * 20);\n max-height: none;\n flex: 1;\n}\n\n/* The title row of the dark panel and of the side flyout. */\n.PayloadHeader {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n padding-block: calc(var(--lea-spacing) * 1.25);\n padding-inline: calc(var(--lea-spacing) * 1.75);\n border-bottom: 1px solid var(--lea-color-neutral-800);\n color: var(--lea-palette-text-contrast);\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-semibold);\n gap: var(--lea-spacing-1);\n}\n\n/*\n * The payload itself. Code, so it is the one place with a family of its own:\n * the monospace font of the system. 1.6 is roomier than `normal`, so the lines\n * of a long payload are easy to follow.\n */\n.PayloadCode {\n overflow: auto;\n max-height: calc(var(--lea-spacing) * 40);\n padding: calc(var(--lea-spacing) * 1.75);\n margin: 0;\n color: var(--lea-color-neutral-100);\n font-family:\n ui-monospace, SFMono-Regular, Menlo, Consolas, \"Liberation Mono\", monospace;\n line-height: 1.6;\n white-space: pre;\n}\n\n/*\n * The text box the payload is edited in, the same box as the code it stands\n * for. It has no border on the dark panel; its ring shows focus, and an\n * invalid edit is ringed in the error colour inside the box, so nothing moves.\n */\n.PayloadEditor {\n display: block;\n width: 100%;\n min-height: calc(var(--lea-spacing) * 20);\n max-height: calc(var(--lea-spacing) * 40);\n box-sizing: border-box;\n padding: calc(var(--lea-spacing) * 1.75);\n border: none;\n margin: 0;\n background-color: var(--lea-color-neutral-950);\n color: var(--lea-color-neutral-100);\n font-family:\n ui-monospace, SFMono-Regular, Menlo, Consolas, \"Liberation Mono\", monospace;\n line-height: 1.6;\n resize: vertical;\n white-space: pre;\n}\n\n.PayloadEditor[aria-invalid=\"true\"] {\n box-shadow: inset 0 0 0 2px var(--lea-palette-status-error-base);\n}\n\n.PayloadEditor:focus-visible {\n outline: 2px solid var(--lea-palette-text-contrast);\n outline-offset: -2px;\n}\n\n/* Why the edit was not applied. red-300 is 7.9:1 against neutral-950. */\n.PayloadError {\n padding-block: var(--lea-spacing-1) 0;\n padding-inline: calc(var(--lea-spacing) * 1.75);\n margin: 0;\n color: var(--lea-color-red-300);\n line-height: 1.6;\n overflow-wrap: anywhere;\n}\n\n/* Cancel and Apply under the text box. */\n.PayloadActions {\n display: flex;\n flex-flow: row wrap;\n justify-content: flex-end;\n padding: var(--lea-spacing-1-5);\n border-top: 1px solid var(--lea-color-neutral-800);\n margin-block-start: var(--lea-spacing-1);\n background-color: var(--lea-color-neutral-900);\n gap: var(--lea-spacing-1);\n}\n\n/* In the flyout the text box fills the height that is left. */\n.Flyout .PayloadEditor {\n min-height: 0;\n max-height: none;\n flex: 1;\n resize: none;\n}\n\n/*\n * The syntax-highlighted payload. Every shade clears 4.5:1 against\n * neutral-950, so highlighting never trades contrast for colour.\n */\n.Token[data-token=\"key\"] {\n color: var(--lea-color-neutral-100);\n}\n\n.Token[data-token=\"string\"] {\n color: var(--lea-color-teal-300);\n}\n\n.Token[data-token=\"number\"] {\n color: var(--lea-color-orange-300);\n}\n\n.Token[data-token=\"boolean\"] {\n color: var(--lea-color-blue-300);\n}\n\n.Token[data-token=\"null\"] {\n color: var(--lea-color-purple-300);\n}\n\n.Token[data-token=\"punctuation\"] {\n color: var(--lea-color-neutral-400);\n}\n\n/* ----------------------------------------------------------------------------\n * Payload side flyout (`placement=\"side\"`)\n * ------------------------------------------------------------------------- */\n\n/*\n * The floating dark panel that opens beside the dev panel. It is rendered in\n * a portal on the body and placed inline (top, left, width, height) from the\n * rect of the panel, so it can sit outside the panel's clipped bounds. The\n * layer is the one `Dialog` and `Drawer` use.\n */\n.Flyout {\n position: fixed;\n z-index: 1300;\n display: flex;\n overflow: hidden;\n flex-direction: column;\n border-radius: var(--lea-radius-4xl);\n background-color: var(--lea-color-neutral-950);\n box-shadow: var(--lea-shadow-2xl);\n color: var(--lea-palette-text-contrast);\n /* It takes focus only when nothing inside it can; the dialog itself has no ring. */\n outline: none;\n}\n\n.FlyoutTitle {\n display: inline-flex;\n min-width: 0;\n align-items: center;\n gap: var(--lea-spacing-1);\n}\n\n/* The payload in the flyout fills the height that is left and scrolls. */\n.Flyout .PayloadCode {\n min-height: 0;\n max-height: none;\n flex: 1;\n}\n\n.FlyoutFooter {\n padding: var(--lea-spacing-1-5);\n border-top: 1px solid var(--lea-color-neutral-800);\n background-color: var(--lea-color-neutral-900);\n}\n\n/* The full-width \"Copy JSON\" action at the foot of the flyout. */\n.FlyoutCopyButton {\n display: inline-flex;\n width: 100%;\n align-items: center;\n justify-content: center;\n padding-block: calc(var(--lea-spacing) * 1.25);\n padding-inline: var(--lea-spacing-1-5);\n border: 1px solid var(--lea-color-neutral-700);\n border-radius: var(--lea-radius-lg);\n margin: 0;\n background-color: var(--lea-color-neutral-800);\n color: var(--lea-palette-text-contrast);\n cursor: pointer;\n font-family: inherit;\n font-size: var(--lea-font-step-minus-1);\n font-weight: var(--lea-font-weight-semibold);\n gap: var(--lea-spacing-1);\n transition: background-color var(--lea-duration-normal)\n var(--lea-easing-standard);\n}\n\n.FlyoutCopyButton svg {\n display: block;\n}\n\n@media (hover: hover) {\n .FlyoutCopyButton:hover {\n background-color: var(--lea-color-neutral-700);\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Footer actions\n * ------------------------------------------------------------------------- */\n\n/*\n * The action row. The negative margins cancel the padding of `.Body`, so as\n * the last child the footer sits flush with the edges of the panel under a\n * full-width divider.\n */\n.Footer {\n display: flex;\n flex-flow: row wrap;\n align-items: center;\n padding-block: var(--lea-spacing-2);\n padding-inline: var(--lea-spacing-2-5);\n border-top: 1px solid var(--lea-palette-border-subtle);\n margin-block: var(--lea-spacing-0-5) calc(var(--lea-spacing-2-5) * -1);\n margin-inline: calc(var(--lea-spacing-2-5) * -1);\n background-color: var(--lea-color-beige-100);\n gap: calc(var(--lea-spacing) * 1.25);\n}\n\n/* ----------------------------------------------------------------------------\n * Shared\n * ------------------------------------------------------------------------- */\n\n.SegmentedOption:focus-visible,\n.Chip:focus-visible,\n.StepButton:focus-visible,\n.StatusCard:focus-visible,\n.PayloadToggle:focus-visible,\n.FlyoutCopyButton:focus-visible {\n outline: 2px solid var(--lea-palette-text-base);\n}\n\n/*\n * On the dark payload panels the ring is white: text-base does not show on\n * neutral-950. `.OnDark` is on the library's `IconButton`s in those panels,\n * and the panel class in front lifts the rule above the button's own ring,\n * whichever stylesheet the app happens to load last.\n */\n.FlyoutCopyButton:focus-visible,\n.PayloadPanel .OnDark:focus-visible,\n.Flyout .OnDark:focus-visible {\n outline-color: var(--lea-palette-text-contrast);\n}\n\n/* Text for assistive tech only: the live regions that confirm a copy. */\n.VisuallyHidden {\n position: absolute;\n overflow: hidden;\n width: 1px;\n height: 1px;\n padding: 0;\n border: 0;\n margin: -1px;\n clip: rect(0 0 0 0);\n white-space: nowrap;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .SegmentedOption,\n .Chip,\n .StepCircle,\n .StatusCard,\n .PayloadChevron,\n .FlyoutCopyButton {\n transition-duration: 0.01ms;\n }\n}\n","import { createTranslation } from \"../index\";\n\n/**\n * Translated accessible names and status words used across the `DevPanel`\n * sub-components (copy buttons, the minimise control, and the per-step status\n * announced to assistive tech). Every string is overridable per-instance via a\n * matching prop, so consumers can localise or reword without forking.\n */\nexport const useDevPanelLabels = createTranslation({\n copy: { no: \"Kopier\", se: \"Kopiera\", fi: \"Kopioi\", es: \"Copiar\" },\n copied: { no: \"Kopiert\", se: \"Kopierad\", fi: \"Kopioitu\", es: \"Copiado\" },\n copyJson: {\n no: \"Kopier JSON\",\n se: \"Kopiera JSON\",\n fi: \"Kopioi JSON\",\n es: \"Copiar JSON\",\n },\n close: { no: \"Lukk\", se: \"Stäng\", fi: \"Sulje\", es: \"Cerrar\" },\n edit: { no: \"Rediger\", se: \"Redigera\", fi: \"Muokkaa\", es: \"Editar\" },\n apply: { no: \"Bruk\", se: \"Tillämpa\", fi: \"Käytä\", es: \"Aplicar\" },\n cancel: { no: \"Avbryt\", se: \"Avbryt\", fi: \"Peruuta\", es: \"Cancelar\" },\n reset: {\n no: \"Tilbakestill\",\n se: \"Återställ\",\n fi: \"Palauta\",\n es: \"Restablecer\",\n },\n invalidJson: {\n no: \"Ugyldig JSON\",\n se: \"Ogiltig JSON\",\n fi: \"Virheellinen JSON\",\n es: \"JSON no válido\",\n },\n minimise: {\n no: \"Minimer\",\n se: \"Minimera\",\n fi: \"Pienennä\",\n es: \"Minimizar\",\n },\n expand: { no: \"Utvid\", se: \"Expandera\", fi: \"Laajenna\", es: \"Expandir\" },\n hide: { no: \"Skjul\", se: \"Dölj\", fi: \"Piilota\", es: \"Ocultar\" },\n show: { no: \"Vis\", se: \"Visa\", fi: \"Näytä\", es: \"Mostrar\" },\n pin: { no: \"Fest\", se: \"Fäst\", fi: \"Kiinnitä\", es: \"Fijar\" },\n step: { no: \"Steg\", se: \"Steg\", fi: \"Vaihe\", es: \"Paso\" },\n statusCompleted: {\n no: \"fullført\",\n se: \"slutförd\",\n fi: \"valmis\",\n es: \"completado\",\n },\n statusCurrent: {\n no: \"gjeldende\",\n se: \"aktuell\",\n fi: \"nykyinen\",\n es: \"actual\",\n },\n statusUpcoming: {\n no: \"ikke startet\",\n se: \"inte påbörjad\",\n fi: \"aloittamatta\",\n es: \"no iniciado\",\n },\n needsAttention: {\n no: \"krever oppmerksomhet\",\n se: \"kräver uppmärksamhet\",\n fi: \"vaatii huomiota\",\n es: \"requiere atención\",\n },\n});\n","\"use client\";\n\nimport { EyeOff, Minus, Pin, Plus } from \"lucide-react\";\nimport {\n createContext,\n useContext,\n useEffect,\n useId,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n type RefObject,\n} from \"react\";\n\nimport { IconButton } from \"../index\";\nimport styles from \"./dev-panel.module.css\";\nimport { useDevPanelLabels } from \"./use-dev-panel-labels\";\n\nconst join = (...parts: (string | undefined)[]) =>\n parts.filter(Boolean).join(\" \");\n\n/**\n * Exposes the current section's heading `id` so an interactive group nested in\n * a `DevPanel.Section` (e.g. a `radiogroup`) can wire `aria-labelledby` to a\n * real, co-located element — the id can never dangle.\n */\nconst DevPanelSectionContext = createContext<string | undefined>(undefined);\n\n/** Reads the enclosing `DevPanel.Section` heading id, if any. */\nexport function useDevPanelSectionLabelId() {\n return useContext(DevPanelSectionContext);\n}\n\n/** What `DevPanel.Handle` needs from the panel it brings back. */\ntype DevPanelRootContextValue = {\n show: () => void;\n title: ReactNode;\n handleRef: RefObject<HTMLButtonElement | null>;\n /** The edge of the window the panel is fixed to, when it has a `position`. */\n side: \"left\" | \"right\" | undefined;\n};\n\nconst DevPanelRootContext = createContext<DevPanelRootContextValue | null>(\n null,\n);\n\n// Below the `sm` breakpoint (600px), where the panel would cover the form.\nconst NARROW_SCREEN = \"(max-width: 599.95px)\";\n\nconst isMac = () =>\n typeof navigator !== \"undefined\" &&\n /mac|iphone|ipad/i.test(navigator.platform || navigator.userAgent);\n\n/**\n * Whether a key press is the shortcut, written as `\"Mod+Shift+D\"`: the key\n * last, after any of `Mod` (⌘ on a Mac, Ctrl elsewhere), `Ctrl`, `Meta`,\n * `Alt` and `Shift`. A modifier not named must not be held.\n */\nexport function matchesShortcut(\n event: KeyboardEvent,\n shortcut: string,\n): boolean {\n const parts = shortcut.split(\"+\").map((part) => part.trim().toLowerCase());\n const key = parts.pop();\n if (!key) return false;\n\n const mod = parts.includes(\"mod\");\n const wants = {\n ctrl: parts.includes(\"ctrl\") || (mod && !isMac()),\n meta: parts.includes(\"meta\") || (mod && isMac()),\n alt: parts.includes(\"alt\"),\n shift: parts.includes(\"shift\"),\n };\n if (\n event.ctrlKey !== wants.ctrl ||\n event.metaKey !== wants.meta ||\n event.altKey !== wants.alt ||\n event.shiftKey !== wants.shift\n )\n return false;\n\n // By the physical key as well, as Alt and Shift change `key` (\"∂\", \"D\").\n return (\n event.key.toLowerCase() === key ||\n event.code.toLowerCase() === `key${key}` ||\n event.code.toLowerCase() === `digit${key}`\n );\n}\n\nexport type DevPanelPosition = \"bottom-left\" | \"bottom-right\";\n\nexport type DevPanelProps = {\n /** Panel title shown in the header. Defaults to `\"Dev controls\"`. */\n title?: ReactNode;\n /** Optional pill beside the title, e.g. `\"MOCK\"` or `\"live\"`. */\n badge?: ReactNode;\n /** Show the small status dot before the title. Defaults to `true`. */\n showStatusDot?: boolean;\n /** Render the minimise control. Defaults to `true`. */\n minimisable?: boolean;\n /** Controlled minimised state. Omit for uncontrolled. */\n minimised?: boolean;\n /** Initial minimised state when uncontrolled. Defaults to `false`. */\n defaultMinimised?: boolean;\n /** Called whenever the minimised state changes. */\n onMinimisedChange?: (minimised: boolean) => void;\n /** Accessible name for the minimise button. Defaults to a translated label. */\n minimiseLabel?: string;\n /** Accessible name for the restore button. Defaults to a translated label. */\n expandLabel?: string;\n /**\n * Lets the panel be hidden completely: a hide button joins the minimise\n * button, `shortcut` toggles it, and while it is hidden `handle` (a small\n * tab at the edge of the window) brings it back. On a narrow screen the\n * panel also starts minimised, drawn as a small round button. Defaults to\n * `true` when `hidden` or `defaultHidden` is passed.\n */\n hideable?: boolean;\n /** Controlled hidden state. Omit for uncontrolled. */\n hidden?: boolean;\n /** Initial hidden state when uncontrolled. Defaults to `false`. */\n defaultHidden?: boolean;\n /** Called whenever the hidden state changes. Store it to keep it across reloads. */\n onHiddenChange?: (hidden: boolean) => void;\n /**\n * The keys that hide and show the panel, anywhere on the page. `Mod` is ⌘\n * on a Mac and Ctrl elsewhere. `false` turns it off. Panels that share a\n * shortcut hide and show together; give one its own to toggle it alone.\n * @default \"Mod+Shift+D\"\n */\n shortcut?: string | false;\n /** What shows while the panel is hidden. Defaults to `<DevPanel.Handle />`. */\n handle?: ReactNode;\n /** Accessible name for the hide button. Defaults to a translated label. */\n hideLabel?: string;\n /**\n * Fixes the panel to a corner of the window, floating above the app, so two\n * panels can sit on either side of the page. While hidden, its handle sits\n * on the same side. Omit to place the panel yourself.\n */\n position?: DevPanelPosition;\n /**\n * Adds a pin button that docks a panel with a `position` to its side of the\n * window, at full height, from 900px wide. The app makes room for it, as in\n * the Pinned story. Defaults to `true` when `pinned` or `defaultPinned` is\n * passed.\n */\n pinnable?: boolean;\n /** Controlled pinned state. Omit for uncontrolled. */\n pinned?: boolean;\n /** Initial pinned state when uncontrolled. Defaults to `false`. */\n defaultPinned?: boolean;\n /** Called whenever the pinned state changes. Store it to keep it across reloads. */\n onPinnedChange?: (pinned: boolean) => void;\n /** Accessible name for the pin button. Defaults to a translated label. */\n pinLabel?: string;\n /** Section content: compose the `DevPanel.*` sub-components here. */\n children?: ReactNode;\n /** Class on the outermost element, for placing the panel. */\n className?: string;\n /** Inline style on the outermost element, for placing the panel. */\n style?: CSSProperties;\n};\n\n/**\n * The root of the reusable dev panel: a self-contained, content-only card that\n * form apps compose from the `DevPanel.*` sub-components. It owns the chrome\n * (title, optional badge, minimise control) and layout; every section is fully\n * controlled by the consumer via `value` + `onChange`/callback props.\n *\n * It renders only the panel, and placement is left to the consuming app,\n * unless `position` fixes it to a corner of the window. Two panels can then\n * sit on either side of the page:\n *\n * ```tsx\n * <DevPanel.Root badge=\"MOCK\">\n * <DevPanel.Country value={country} onChange={setCountry} options={countries} />\n * <DevPanel.Status value={status} onChange={setStatus} options={statuses} />\n * <DevPanel.Payload value={payload} />\n * <DevPanel.Footer onReseed={reseed} onCopyUrl={() => {}} url={url} />\n * </DevPanel.Root>\n *\n * <DevPanel.Root title=\"Payload\" position=\"bottom-left\">\n * <DevPanel.Payload placement=\"panel\" value={payload} onApply={setOverride} />\n * </DevPanel.Root>\n * ```\n */\nexport function DevPanel({\n title = \"Dev controls\",\n badge,\n showStatusDot = true,\n minimisable = true,\n minimised,\n defaultMinimised,\n onMinimisedChange,\n minimiseLabel,\n expandLabel,\n hidden,\n defaultHidden,\n onHiddenChange,\n hideable = hidden !== undefined || defaultHidden !== undefined,\n shortcut = \"Mod+Shift+D\",\n handle,\n hideLabel,\n position,\n pinned,\n defaultPinned,\n onPinnedChange,\n pinnable = pinned !== undefined || defaultPinned !== undefined,\n pinLabel,\n children,\n className,\n style,\n}: DevPanelProps) {\n const t = useDevPanelLabels();\n const isControlled = minimised !== undefined;\n const [internalMinimised, setInternalMinimised] = useState(\n defaultMinimised ?? false,\n );\n const isMinimised = isControlled ? minimised : internalMinimised;\n\n const [internalHidden, setInternalHidden] = useState(defaultHidden ?? false);\n const isHidden = hideable && (hidden ?? internalHidden);\n\n const [internalPinned, setInternalPinned] = useState(defaultPinned ?? false);\n const isPinned = pinned ?? internalPinned;\n const canPin = pinnable && position !== undefined;\n\n const rootRef = useRef<HTMLDivElement>(null);\n const handleRef = useRef<HTMLButtonElement>(null);\n const hideButtonRef = useRef<HTMLButtonElement>(null);\n // Where focus goes once the panel or the handle has rendered.\n const pendingFocus = useRef<\"handle\" | \"panel\" | null>(null);\n\n const toggleMinimised = () => {\n const next = !isMinimised;\n if (!isControlled) setInternalMinimised(next);\n onMinimisedChange?.(next);\n };\n\n const togglePinned = () => {\n const next = !isPinned;\n if (pinned === undefined) setInternalPinned(next);\n onPinnedChange?.(next);\n };\n\n const setHiddenState = (next: boolean) => {\n if (hidden === undefined) setInternalHidden(next);\n onHiddenChange?.(next);\n };\n\n // A hideable panel on a narrow screen starts minimised, unless told otherwise.\n useEffect(() => {\n if (\n hideable &&\n !isControlled &&\n defaultMinimised === undefined &&\n typeof window !== \"undefined\" &&\n window.matchMedia?.(NARROW_SCREEN).matches\n )\n setInternalMinimised(true);\n // Once, as a default: not again when the window is resized.\n }, []);\n\n useEffect(() => {\n const target = pendingFocus.current;\n pendingFocus.current = null;\n if (target === \"handle\") handleRef.current?.focus();\n if (target === \"panel\") hideButtonRef.current?.focus();\n });\n\n // The latest state for the listener, which is added once.\n const latest = useRef({ isHidden, setHiddenState });\n latest.current = { isHidden, setHiddenState };\n\n useEffect(() => {\n if (!hideable || shortcut === false) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.repeat || !matchesShortcut(event, shortcut)) return;\n event.preventDefault();\n const { isHidden: wasHidden, setHiddenState: set } = latest.current;\n const active = document.activeElement;\n // Focus follows only when it was in what goes away.\n if (!wasHidden && rootRef.current?.contains(active))\n pendingFocus.current = \"handle\";\n if (wasHidden && active != null && active === handleRef.current)\n pendingFocus.current = \"panel\";\n set(!wasHidden);\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, [hideable, shortcut]);\n\n const bodyId = useId();\n const side =\n position === undefined\n ? undefined\n : position.endsWith(\"left\")\n ? \"left\"\n : \"right\";\n\n if (isHidden) {\n return (\n <DevPanelRootContext.Provider\n value={{\n show: () => {\n pendingFocus.current = \"panel\";\n setHiddenState(false);\n },\n title,\n handleRef,\n side,\n }}\n >\n {handle ?? <DevPanelHandle />}\n </DevPanelRootContext.Provider>\n );\n }\n\n return (\n <div\n ref={rootRef}\n data-dev-panel-root=\"\"\n data-position={position}\n data-hideable={hideable ? \"\" : undefined}\n data-minimised={isMinimised ? \"\" : undefined}\n // Docked only while open: minimised, it floats as before.\n data-pinned={canPin && isPinned && !isMinimised ? \"\" : undefined}\n className={join(styles.DevPanel, className)}\n style={style}\n >\n {/* A `div`, not a `header`: outside a section that would be a page\n banner, and an app has its own (and may show two panels). */}\n <div className={styles.Header}>\n <span className={styles.HeaderTitle}>\n {showStatusDot && <span className={styles.HeaderDot} aria-hidden />}\n <span>{title}</span>\n {badge != null && <span className={styles.Badge}>{badge}</span>}\n </span>\n <span className={styles.HeaderActions}>\n {canPin && !isMinimised && (\n <IconButton\n className={styles.PinButton}\n aria-label={pinLabel ?? t.pin}\n aria-pressed={isPinned}\n onClick={togglePinned}\n variant={isPinned ? \"solid\" : \"outline\"}\n color=\"secondary\"\n size=\"sm\"\n >\n <Pin size={18} aria-hidden />\n </IconButton>\n )}\n {hideable && (\n <IconButton\n ref={hideButtonRef}\n className={styles.HideButton}\n aria-label={hideLabel ?? t.hide}\n onClick={() => {\n pendingFocus.current = \"handle\";\n setHiddenState(true);\n }}\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n >\n <EyeOff size={18} aria-hidden />\n </IconButton>\n )}\n {minimisable && (\n <IconButton\n aria-label={\n isMinimised\n ? (expandLabel ?? t.expand)\n : (minimiseLabel ?? t.minimise)\n }\n aria-expanded={!isMinimised}\n aria-controls={bodyId}\n onClick={toggleMinimised}\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n >\n {isMinimised ? (\n <Plus size={18} aria-hidden />\n ) : (\n <Minus size={18} aria-hidden />\n )}\n </IconButton>\n )}\n </span>\n </div>\n {!isMinimised && (\n <div id={bodyId} className={styles.Body}>\n {children}\n </div>\n )}\n </div>\n );\n}\n\nexport type DevPanelSectionProps = {\n /** The uppercase section heading (e.g. `\"Country\"`). */\n label: ReactNode;\n /** Optional trailing meta shown on the heading row (e.g. a `6 / 10` count). */\n meta?: ReactNode;\n children?: ReactNode;\n /** Class on the `section` element. */\n className?: string;\n /** Inline style on the `section` element. */\n style?: CSSProperties;\n};\n\n/**\n * A labelled section wrapper. Renders the uppercase heading and exposes its\n * generated id through context so a nested interactive group can reference it\n * via `aria-labelledby`. Used internally by every `DevPanel.*` section;\n * exported so consumers can build bespoke sections with the same look and\n * wiring.\n */\nexport function DevPanelSection({\n label,\n meta,\n children,\n className,\n style,\n}: DevPanelSectionProps) {\n const labelId = useId();\n\n return (\n <section\n aria-labelledby={labelId}\n className={join(styles.Section, className)}\n style={style}\n >\n <div className={styles.SectionHeaderRow}>\n <span id={labelId} className={styles.SectionLabel}>\n {label}\n </span>\n {meta != null && <span className={styles.SectionMeta}>{meta}</span>}\n </div>\n <DevPanelSectionContext.Provider value={labelId}>\n {children}\n </DevPanelSectionContext.Provider>\n </section>\n );\n}\n\nexport type DevPanelHandleProps = {\n /**\n * The edge of the window the tab sits on. Defaults to the side of the\n * panel's `position`, or `\"right\"`.\n */\n side?: \"left\" | \"right\";\n /** The text on the tab. Defaults to the panel's `title`. */\n label?: ReactNode;\n /** Class on the button, for placing it differently. */\n className?: string;\n /** Inline style on the button, for placing it differently. */\n style?: CSSProperties;\n};\n\n/**\n * The tab that stands in for a hidden `DevPanel.Root` and brings it back: a\n * small button fixed to the bottom of the right (or left) edge of the window.\n * The panel renders it while hidden, so it is only used as the panel's\n * `handle`, to change its side or text:\n *\n * ```tsx\n * <DevPanel.Root hideable handle={<DevPanel.Handle side=\"left\" />}>\n * ```\n */\nexport function DevPanelHandle({\n side: sideProp,\n label,\n className,\n style,\n}: DevPanelHandleProps) {\n const t = useDevPanelLabels();\n const root = useContext(DevPanelRootContext);\n if (!root) return null;\n\n const text = label ?? root.title;\n const side = sideProp ?? root.side ?? \"right\";\n\n return (\n <button\n ref={root.handleRef}\n type=\"button\"\n className={join(styles.Handle, className)}\n style={style}\n data-side={side}\n // The visible text, with what pressing it does in front.\n aria-label={typeof text === \"string\" ? `${t.show} ${text}` : undefined}\n onClick={root.show}\n >\n <span className={styles.HeaderDot} aria-hidden />\n <span>{text}</span>\n </button>\n );\n}\n","\"use client\";\n\nimport {\n useCallback,\n useRef,\n type KeyboardEvent,\n type RefCallback,\n} from \"react\";\n\ntype RovingRadioItemProps = {\n // A callback, so it fits whichever element the option is drawn as.\n ref: RefCallback<HTMLElement>;\n role: \"radio\";\n \"aria-checked\": boolean;\n \"aria-disabled\": true | undefined;\n tabIndex: 0 | -1;\n onClick: () => void;\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;\n};\n\ntype UseRovingRadioOptions<T> = {\n /** All selectable values, in DOM order. */\n values: readonly T[];\n /** The currently selected value (or `undefined` when nothing is selected). */\n selected: T | undefined;\n /** Called with the value to select on click / keyboard activation. */\n onSelect: (value: T) => void;\n /** Optional per-value disabled predicate; disabled items are skipped. */\n isDisabled?: (value: T, index: number) => boolean;\n};\n\n/**\n * Wires the WAI-ARIA radiogroup keyboard pattern for a set of custom option\n * elements: a single roving `tabIndex`, arrow / Home / End navigation that moves\n * focus *and* selection (activation follows focus), and Space / Enter to\n * (re)select the focused item. Disabled items are skipped.\n *\n * The consuming component owns the markup and styling; it just spreads\n * `getItemProps(index)` onto each option and renders the group with\n * `role=\"radiogroup\"`.\n */\nexport function useRovingRadio<T>({\n values,\n selected,\n onSelect,\n isDisabled,\n}: UseRovingRadioOptions<T>) {\n const refs = useRef<(HTMLElement | null)[]>([]);\n\n const disabledAt = useCallback(\n (index: number) => isDisabled?.(values[index], index) ?? false,\n [isDisabled, values],\n );\n\n const selectedIndex = values.findIndex((value) => value === selected);\n const firstEnabledIndex = values.findIndex((_, index) => !disabledAt(index));\n // Exactly one item is tabbable: the selected one, or the first enabled option\n // when there is no selection yet.\n const tabbableIndex =\n selectedIndex >= 0 && !disabledAt(selectedIndex)\n ? selectedIndex\n : firstEnabledIndex;\n\n const focusAndSelect = useCallback(\n (index: number) => {\n refs.current[index]?.focus();\n onSelect(values[index]);\n },\n [onSelect, values],\n );\n\n const nextEnabled = useCallback(\n (from: number, direction: 1 | -1) => {\n const count = values.length;\n for (let step = 1; step <= count; step += 1) {\n const index = (from + direction * step + count * step) % count;\n if (!disabledAt(index)) return index;\n }\n return from;\n },\n [disabledAt, values.length],\n );\n\n const getItemProps = useCallback(\n (index: number): RovingRadioItemProps => {\n const value = values[index];\n const disabled = disabledAt(index);\n\n return {\n ref: (element: HTMLElement | null) => {\n refs.current[index] = element;\n },\n role: \"radio\",\n \"aria-checked\": value === selected,\n \"aria-disabled\": disabled || undefined,\n tabIndex: index === tabbableIndex ? 0 : -1,\n onClick: () => {\n if (!disabled) onSelect(value);\n },\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => {\n switch (event.key) {\n case \"ArrowRight\":\n case \"ArrowDown\":\n event.preventDefault();\n focusAndSelect(nextEnabled(index, 1));\n break;\n case \"ArrowLeft\":\n case \"ArrowUp\":\n event.preventDefault();\n focusAndSelect(nextEnabled(index, -1));\n break;\n case \"Home\":\n event.preventDefault();\n focusAndSelect(nextEnabled(-1, 1));\n break;\n case \"End\":\n event.preventDefault();\n focusAndSelect(nextEnabled(0, -1));\n break;\n case \" \":\n case \"Enter\":\n event.preventDefault();\n if (!disabled) onSelect(value);\n break;\n default:\n break;\n }\n },\n };\n },\n [\n disabledAt,\n focusAndSelect,\n nextEnabled,\n onSelect,\n selected,\n tabbableIndex,\n values,\n ],\n );\n\n return { getItemProps };\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { useDevPanelSectionLabelId } from \"./dev-panel\";\nimport styles from \"./dev-panel.module.css\";\nimport { useRovingRadio } from \"./use-roving-radio\";\n\n/** A single option in a segmented single-select. */\nexport type SegmentedOption<T extends string> = {\n /** The value reported to `onChange` when this option is selected. */\n value: T;\n /** Visible label. */\n label: ReactNode;\n /** Optional leading icon (mark decorative icons `aria-hidden`). */\n icon?: ReactNode;\n disabled?: boolean;\n};\n\n/**\n * Requires an accessible name for the `radiogroup`. When rendered inside a\n * `DevPanel.Section` the group is labelled by the section heading\n * automatically, so a name is only required when used standalone.\n */\ntype SegmentedAccessibleName =\n | { \"aria-label\"?: string; \"aria-labelledby\"?: string };\n\nexport type SegmentedRadioGroupProps<T extends string> = {\n /** The selected value (fully controlled). */\n value: T;\n /** Called with the newly selected value. */\n onChange: (value: T) => void;\n options: SegmentedOption<T>[];\n /**\n * `\"pill\"` (default) — options fill the beige track, selected flips to a\n * white pill. `\"chip\"` — options hug their content and carry a small leading\n * dot.\n */\n variant?: \"pill\" | \"chip\";\n} & SegmentedAccessibleName;\n\n/**\n * An accessible single-select segmented control built on the roving-radiogroup\n * pattern. Internal to `DevPanel`; Country / Variant / Step state compose it.\n */\nexport function SegmentedRadioGroup<T extends string>({\n value,\n onChange,\n options,\n variant = \"pill\",\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n}: SegmentedRadioGroupProps<T>) {\n const sectionLabelId = useDevPanelSectionLabelId();\n const labelledBy = ariaLabelledby ?? (ariaLabel ? undefined : sectionLabelId);\n\n const { getItemProps } = useRovingRadio<T>({\n values: options.map((option) => option.value),\n selected: value,\n onSelect: onChange,\n isDisabled: (_, index) => options[index]?.disabled ?? false,\n });\n\n const isChip = variant === \"chip\";\n\n return (\n <div\n role=\"radiogroup\"\n aria-label={ariaLabel}\n aria-labelledby={labelledBy}\n className={isChip ? styles.ChipGroup : styles.SegmentedGroup}\n >\n {options.map((option, index) => {\n // The chosen option is styled from `aria-checked`, which is in here.\n const { onKeyDown, ...itemProps } = getItemProps(index);\n return (\n <button\n key={option.value}\n type=\"button\"\n data-disabled={option.disabled ? \"\" : undefined}\n onKeyDown={onKeyDown}\n {...itemProps}\n className={isChip ? styles.Chip : styles.SegmentedOption}\n >\n {isChip ? (\n <span className={styles.ChipDot} aria-hidden />\n ) : (\n option.icon\n )}\n <span>{option.label}</span>\n </button>\n );\n })}\n </div>\n );\n}\n","import type { ReactNode } from \"react\";\n\nimport { DevPanelSection } from \"./dev-panel\";\nimport styles from \"./dev-panel.module.css\";\nimport {\n SegmentedRadioGroup,\n type SegmentedOption,\n} from \"./segmented-control\";\n\n/** A selectable country. Provide between 2 and 4. */\nexport type DevPanelCountryOption<T extends string> = {\n /** The value reported to `onChange` (e.g. an ISO code). */\n value: T;\n /** Country name shown in the pill. */\n label: ReactNode;\n /** Optional short code shown, muted, before the name (e.g. `\"SE\"`). */\n code?: ReactNode;\n disabled?: boolean;\n};\n\nexport type DevPanelCountryProps<T extends string> = {\n /** The selected country (fully controlled). */\n value: T;\n /** Called with the newly selected country. */\n onChange: (value: T) => void;\n /** 2-4 country options. */\n options: DevPanelCountryOption<T>[];\n /** Section heading. Defaults to `\"Country\"`. */\n label?: ReactNode;\n};\n\n/**\n * Segmented country selector. Renders each option as a pill with an optional\n * muted code before the name. Single-select and fully controlled.\n */\nexport function DevPanelCountry<T extends string>({\n value,\n onChange,\n options,\n label = \"Country\",\n}: DevPanelCountryProps<T>) {\n const segmentedOptions: SegmentedOption<T>[] = options.map((option) => ({\n value: option.value,\n disabled: option.disabled,\n label: option.label,\n icon:\n option.code != null ? (\n <span className={styles.CountryCode}>{option.code}</span>\n ) : undefined,\n }));\n\n return (\n <DevPanelSection label={label}>\n <SegmentedRadioGroup\n value={value}\n onChange={onChange}\n options={segmentedOptions}\n />\n </DevPanelSection>\n );\n}\n","import type { ReactNode } from \"react\";\n\nimport { DevPanelSection } from \"./dev-panel\";\nimport {\n SegmentedRadioGroup,\n type SegmentedOption,\n} from \"./segmented-control\";\n\n/** A selectable flow variant. */\nexport type DevPanelVariantOption<T extends string> = {\n value: T;\n label: ReactNode;\n disabled?: boolean;\n};\n\nexport type DevPanelVariantProps<T extends string> = {\n /** The selected variant (fully controlled). */\n value: T;\n /** Called with the newly selected variant. */\n onChange: (value: T) => void;\n options: DevPanelVariantOption<T>[];\n /** Section heading. Defaults to `\"Variant\"`. */\n label?: ReactNode;\n};\n\n/**\n * Segmented flow-variant selector (e.g. cash loan / refinance / top-up).\n * Single-select and fully controlled.\n */\nexport function DevPanelVariant<T extends string>({\n value,\n onChange,\n options,\n label = \"Variant\",\n}: DevPanelVariantProps<T>) {\n const segmentedOptions: SegmentedOption<T>[] = options.map((option) => ({\n value: option.value,\n label: option.label,\n disabled: option.disabled,\n }));\n\n return (\n <DevPanelSection label={label}>\n <SegmentedRadioGroup\n value={value}\n onChange={onChange}\n options={segmentedOptions}\n />\n </DevPanelSection>\n );\n}\n","import type { ReactNode } from \"react\";\n\nimport { Text } from \"../index\";\nimport { DevPanelSection } from \"./dev-panel\";\nimport styles from \"./dev-panel.module.css\";\nimport {\n SegmentedRadioGroup,\n type SegmentedOption,\n} from \"./segmented-control\";\n\n/** A selectable variation of the current step. */\nexport type DevPanelStepStateOption<T extends string> = {\n value: T;\n label: ReactNode;\n disabled?: boolean;\n};\n\nexport type DevPanelStepStateProps<T extends string> = {\n /** The selected step-state variation (fully controlled). */\n value: T;\n /** Called with the newly selected variation. */\n onChange: (value: T) => void;\n options: DevPanelStepStateOption<T>[];\n /** Section heading. Defaults to `\"Step state\"`. */\n label?: ReactNode;\n /**\n * Name of the step the variations belong to (e.g. `\"Skulder\"`). When set, a\n * `\"{variationsLabel} {stepLabel}\"` line is shown above the chips.\n */\n stepLabel?: ReactNode;\n /** Prefix for the step-label line. Defaults to `\"Variations for\"`. */\n variationsLabel?: ReactNode;\n};\n\n/**\n * Segmented chips for the different states/variations of the current step (e.g.\n * required debts / voluntary / no debts). Single-select and fully controlled.\n */\nexport function DevPanelStepState<T extends string>({\n value,\n onChange,\n options,\n label = \"Step state\",\n stepLabel,\n variationsLabel = \"Variations for\",\n}: DevPanelStepStateProps<T>) {\n const segmentedOptions: SegmentedOption<T>[] = options.map((option) => ({\n value: option.value,\n label: option.label,\n disabled: option.disabled,\n }));\n\n return (\n <DevPanelSection label={label}>\n {stepLabel != null && (\n <Text\n textStyle=\"body-sm\"\n style={{\n color: \"var(--lea-color-neutral-600)\",\n marginBlockEnd: \"var(--lea-spacing-0-5)\",\n }}\n >\n {variationsLabel}{\" \"}\n <span className={styles.StepStateName}>{stepLabel}</span>\n </Text>\n )}\n <SegmentedRadioGroup\n value={value}\n onChange={onChange}\n options={segmentedOptions}\n variant=\"chip\"\n />\n </DevPanelSection>\n );\n}\n","\"use client\";\n\nimport { Check } from \"lucide-react\";\nimport type { CSSProperties, ReactNode } from \"react\";\n\nimport { useDevPanelSectionLabelId, DevPanelSection } from \"./dev-panel\";\nimport styles from \"./dev-panel.module.css\";\nimport { useDevPanelLabels } from \"./use-dev-panel-labels\";\n\ntype StepCircleState = \"complete\" | \"current\" | \"upcoming\";\n\n/** A single step in the jump-to-step grid. */\nexport type DevPanelStep = {\n /** Short label shown beneath the circle (e.g. `\"Skulder\"`). */\n label: ReactNode;\n /**\n * Explicit visual state. When omitted it is derived from `currentStep`\n * (before → `complete`, at → `current`, after → `upcoming`).\n */\n status?: StepCircleState;\n /** Marks the step with a warning dot (e.g. it needs attention). */\n flagged?: boolean;\n disabled?: boolean;\n};\n\nexport type DevPanelStepsProps = {\n /** The ordered steps. */\n steps: DevPanelStep[];\n /**\n * The active step (1-based). Drives the count and, for any step without an\n * explicit `status`, the derived complete/current/upcoming state.\n */\n currentStep?: number;\n /** Called when a step is chosen, with its 1-based number and the step. */\n onStepSelect?: (step: number, item: DevPanelStep) => void;\n /** Section heading. Defaults to `\"Jump to step\"`. */\n label?: ReactNode;\n /** Show the `currentStep / total` count in the heading. Defaults to `true`. */\n showCount?: boolean;\n /** Number of step circles per row. Defaults to `4`. */\n columns?: number;\n};\n\nconst statusWord = (\n state: StepCircleState,\n t: ReturnType<typeof useDevPanelLabels>,\n) => {\n switch (state) {\n case \"complete\":\n return t.statusCompleted;\n case \"current\":\n return t.statusCurrent;\n default:\n return t.statusUpcoming;\n }\n};\n\n/**\n * A grid of numbered step circles for jumping between form steps. Completed\n * steps show a check, the current step is filled, and a warning dot flags steps\n * that need attention. Fully controlled: the consumer supplies the steps and the\n * active step, and handles selection.\n */\nexport function DevPanelSteps({\n steps,\n currentStep,\n onStepSelect,\n label = \"Jump to step\",\n showCount = true,\n columns = 4,\n}: DevPanelStepsProps) {\n const t = useDevPanelLabels();\n const total = steps.length;\n const meta =\n showCount && currentStep != null ? `${currentStep} / ${total}` : undefined;\n\n return (\n <DevPanelSection label={label} meta={meta}>\n <StepGrid\n steps={steps}\n currentStep={currentStep}\n onStepSelect={onStepSelect}\n columns={columns}\n t={t}\n />\n </DevPanelSection>\n );\n}\n\nfunction StepGrid({\n steps,\n currentStep,\n onStepSelect,\n columns,\n t,\n}: {\n steps: DevPanelStep[];\n currentStep?: number;\n onStepSelect?: (step: number, item: DevPanelStep) => void;\n columns: number;\n t: ReturnType<typeof useDevPanelLabels>;\n}) {\n const groupLabelId = useDevPanelSectionLabelId();\n\n return (\n <div\n role=\"group\"\n aria-labelledby={groupLabelId}\n className={styles.StepGrid}\n style={{ \"--_columns\": columns } as CSSProperties}\n >\n {steps.map((item, index) => {\n const stepNumber = index + 1;\n const state: StepCircleState =\n item.status ??\n (currentStep == null\n ? \"upcoming\"\n : stepNumber < currentStep\n ? \"complete\"\n : stepNumber === currentStep\n ? \"current\"\n : \"upcoming\");\n const isCurrent = state === \"current\";\n const isComplete = state === \"complete\";\n\n // The button is named by what it shows, the number and the label,\n // with the words a sighted user reads from the drawing added for\n // assistive tech: \"Step\" before the number, and the status in\n // brackets after the label, as in \"Steg 6 Skulder (gjeldende)\". An\n // `aria-label` would replace the visible text instead of adding to\n // it, and could not hold a label that is not a string.\n return (\n <button\n key={stepNumber}\n type=\"button\"\n aria-current={isCurrent ? \"step\" : undefined}\n data-state={state}\n data-disabled={item.disabled ? \"\" : undefined}\n disabled={item.disabled}\n onClick={() => onStepSelect?.(stepNumber, item)}\n className={styles.StepButton}\n >\n <span className={styles.StepCircle}>\n <span className={styles.VisuallyHidden}>\n {/* A completed step shows a check in place of its number. */}\n {isComplete ? `${t.step} ${stepNumber}` : `${t.step} `}\n </span>\n {isComplete ? <Check size={18} aria-hidden /> : stepNumber}\n {item.flagged && (\n <span className={styles.StepFlagDot} aria-hidden />\n )}\n </span>{\" \"}\n <span className={styles.StepLabel}>{item.label}</span>\n <span className={styles.VisuallyHidden}>\n {/* Brackets, not a comma: browsers put a space before this\n part of the name, which would leave the comma on its own. */}\n {`(${statusWord(state, t)}${\n item.flagged ? `, ${t.needsAttention}` : \"\"\n })`}\n </span>\n </button>\n );\n })}\n </div>\n );\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { useDevPanelSectionLabelId, DevPanelSection } from \"./dev-panel\";\nimport styles from \"./dev-panel.module.css\";\nimport { useRovingRadio } from \"./use-roving-radio\";\n\n/** The statuses a card can be coloured as. `neutral` is a status with no colour of its own. */\nexport type DevPanelStatusColor =\n | \"warning\"\n | \"success\"\n | \"info\"\n | \"error\"\n | \"neutral\";\n\n/** A selectable application status. */\nexport type DevPanelStatusOption<T extends string> = {\n value: T;\n label: ReactNode;\n /**\n * Colour of the leading dot / selected border. Defaults to `\"neutral\"`. The\n * dot is decorative — status is always conveyed by the label text too, so it\n * never relies on colour alone.\n */\n color?: DevPanelStatusColor;\n disabled?: boolean;\n};\n\nexport type DevPanelStatusProps<T extends string> = {\n /** The selected status (fully controlled). */\n value: T;\n /** Called with the newly selected status. */\n onChange: (value: T) => void;\n options: DevPanelStatusOption<T>[];\n /** Section heading. Defaults to `\"Application status\"`. */\n label?: ReactNode;\n};\n\n/**\n * A radio grid for the application status. Each card carries a decorative\n * coloured dot mapped to a status token; the status is always spelled out in the\n * label so it is never signalled by colour alone. Single-select and fully\n * controlled.\n */\nexport function DevPanelStatus<T extends string>({\n value,\n onChange,\n options,\n label = \"Application status\",\n}: DevPanelStatusProps<T>) {\n return (\n <DevPanelSection label={label}>\n <StatusGrid value={value} onChange={onChange} options={options} />\n </DevPanelSection>\n );\n}\n\nfunction StatusGrid<T extends string>({\n value,\n onChange,\n options,\n}: {\n value: T;\n onChange: (value: T) => void;\n options: DevPanelStatusOption<T>[];\n}) {\n const groupLabelId = useDevPanelSectionLabelId();\n const { getItemProps } = useRovingRadio<T>({\n values: options.map((option) => option.value),\n selected: value,\n onSelect: onChange,\n isDisabled: (_, index) => options[index]?.disabled ?? false,\n });\n\n return (\n <div\n role=\"radiogroup\"\n aria-labelledby={groupLabelId}\n className={styles.StatusGrid}\n >\n {options.map((option, index) => {\n // The chosen card is styled from `aria-checked`, which is in here.\n const { onKeyDown, ...itemProps } = getItemProps(index);\n return (\n <button\n key={option.value}\n type=\"button\"\n data-color={option.color ?? \"neutral\"}\n data-disabled={option.disabled ? \"\" : undefined}\n onKeyDown={onKeyDown}\n {...itemProps}\n className={styles.StatusCard}\n >\n <span className={styles.StatusDot} aria-hidden />\n <span>{option.label}</span>\n </button>\n );\n })}\n </div>\n );\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\n/**\n * Writes text to the clipboard and tracks a short-lived `copied` flag so the\n * caller can announce success (via a `role=\"status\"` live region) and flip an\n * icon. The flag auto-resets after ~2s; the pending timer is cleared on unmount.\n */\nexport function useCopy() {\n const [copied, setCopied] = useState(false);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(\n undefined,\n );\n\n const copy = useCallback(async (text: string) => {\n try {\n await navigator.clipboard?.writeText(text);\n } catch {\n // Clipboard can reject (permissions, insecure context). Still surface the\n // confirmation so the interaction feels responsive; the dev can retry.\n }\n setCopied(true);\n clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => setCopied(false), 2000);\n }, []);\n\n useEffect(() => () => clearTimeout(timeoutRef.current), []);\n\n return { copied, copy };\n}\n","\"use client\";\n\nimport { Check, Copy } from \"lucide-react\";\nimport { Fragment, type ReactNode } from \"react\";\n\nimport { IconButton } from \"../index\";\nimport styles from \"./dev-panel.module.css\";\nimport { useCopy } from \"./use-copy\";\nimport { useDevPanelLabels } from \"./use-dev-panel-labels\";\n\nexport type DevPanelFlowProps = {\n /** Flow identifier (e.g. a short hash). */\n flowId?: ReactNode;\n /** Process name (e.g. `\"loan-se\"`). */\n process?: ReactNode;\n /** Variant name (e.g. `\"refinance\"`). */\n variant?: ReactNode;\n /** Leading label. Defaults to `\"Flow\"`. */\n label?: ReactNode;\n /** Show the copy button. Defaults to `true`. */\n showCopy?: boolean;\n /**\n * String written to the clipboard. Defaults to the string parts joined by\n * `\" · \"`.\n */\n copyValue?: string;\n /** Accessible name for the copy button. Defaults to a translated label. */\n copyLabel?: string;\n /** Called after a successful copy. */\n onCopy?: () => void;\n};\n\nconst asCopyString = (part: ReactNode) =>\n typeof part === \"string\" || typeof part === \"number\" ? String(part) : null;\n\n/**\n * The flow header row: an uppercase label followed by the `id · process ·\n * variant` breadcrumb and a copy button. Copy success is announced via a\n * `role=\"status\"` live region.\n */\nexport function DevPanelFlow({\n flowId,\n process,\n variant,\n label = \"Flow\",\n showCopy = true,\n copyValue,\n copyLabel,\n onCopy,\n}: DevPanelFlowProps) {\n const t = useDevPanelLabels();\n const { copied, copy } = useCopy();\n\n const parts = [flowId, process, variant].filter(\n (part): part is ReactNode => part != null,\n );\n const derivedCopy = parts\n .map(asCopyString)\n .filter((part): part is string => part != null)\n .join(\" · \");\n const textToCopy = copyValue ?? derivedCopy;\n\n const handleCopy = async () => {\n await copy(textToCopy);\n onCopy?.();\n };\n\n return (\n <div className={styles.Flow}>\n <div className={styles.FlowText}>\n <span className={styles.FlowLabel}>{label}</span>\n <span>\n {parts.map((part, index) => (\n <Fragment key={index}>\n {index > 0 && (\n <span aria-hidden className={styles.FlowSeparator}>\n ·\n </span>\n )}\n {part}\n </Fragment>\n ))}\n </span>\n </div>\n {showCopy && (\n <IconButton\n aria-label={copyLabel ?? t.copy}\n onClick={handleCopy}\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n >\n {copied ? (\n <Check size={16} aria-hidden />\n ) : (\n <Copy size={16} aria-hidden />\n )}\n </IconButton>\n )}\n <span role=\"status\" aria-live=\"polite\" className={styles.VisuallyHidden}>\n {copied ? t.copied : \"\"}\n </span>\n </div>\n );\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\n\nimport styles from \"./dev-panel.module.css\";\n\n/** The kinds of token the JSON highlighter distinguishes. */\ntype JsonTokenType =\n | \"key\"\n | \"string\"\n | \"number\"\n | \"boolean\"\n | \"null\"\n | \"punctuation\";\n\ntype Token = { type: JsonTokenType; value: string };\n\n// Matches the value-bearing tokens (strings, numbers, and the JSON keywords).\n// Everything between matches (braces, brackets, commas, colons, whitespace) is\n// treated as punctuation.\nconst TOKEN =\n /\"(?:\\\\.|[^\"\\\\])*\"|-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?|\\b(?:true|false|null)\\b/g;\n\nconst isSpace = (char: string) =>\n char === \" \" || char === \"\\t\" || char === \"\\n\" || char === \"\\r\";\n\nfunction classify(raw: string, code: string, end: number): JsonTokenType {\n if (raw[0] === '\"') {\n // A string is a key when the next non-space character is a colon.\n let i = end;\n while (i < code.length && isSpace(code[i])) i += 1;\n return code[i] === \":\" ? \"key\" : \"string\";\n }\n if (raw === \"true\" || raw === \"false\") return \"boolean\";\n if (raw === \"null\") return \"null\";\n return \"number\";\n}\n\nfunction tokenize(code: string): Token[] {\n const tokens: Token[] = [];\n let lastIndex = 0;\n\n for (const match of code.matchAll(TOKEN)) {\n const index = match.index ?? 0;\n if (index > lastIndex) {\n tokens.push({ type: \"punctuation\", value: code.slice(lastIndex, index) });\n }\n const raw = match[0];\n tokens.push({ type: classify(raw, code, index + raw.length), value: raw });\n lastIndex = index + raw.length;\n }\n\n if (lastIndex < code.length) {\n tokens.push({ type: \"punctuation\", value: code.slice(lastIndex) });\n }\n\n return tokens;\n}\n\n/**\n * Returns `true` when `value` should be syntax-highlighted as JSON: any non-null\n * non-string value (objects, arrays, numbers, booleans), or a string that parses\n * cleanly as JSON. Plain prose strings return `false` so they render verbatim.\n */\nexport function looksLikeJson(value: unknown): boolean {\n if (typeof value !== \"string\") return value != null;\n const trimmed = value.trim();\n if (!trimmed) return false;\n try {\n JSON.parse(trimmed);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Renders a serialised JSON string as coloured spans. Output is built from React\n * nodes (never `dangerouslySetInnerHTML`), so a string value such as `\"<img>\"`\n * is shown as text and can never inject markup. Each span carries its kind as\n * `data-token`, and the stylesheet has one colour for each kind.\n */\nexport function JsonHighlight({ code }: { code: string }) {\n const tokens = useMemo(() => tokenize(code), [code]);\n\n return (\n <>\n {tokens.map((token, index) => (\n <span key={index} className={styles.Token} data-token={token.type}>\n {token.value}\n </span>\n ))}\n </>\n );\n}\n","\"use client\";\n\nimport {\n Braces,\n Check,\n ChevronRight,\n Copy,\n Pencil,\n Undo2,\n X,\n} from \"lucide-react\";\nimport {\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { Button, IconButton } from \"../index\";\nimport styles from \"./dev-panel.module.css\";\nimport { JsonHighlight, looksLikeJson } from \"./json-highlight\";\nimport { useCopy } from \"./use-copy\";\nimport { useDevPanelLabels } from \"./use-dev-panel-labels\";\n\n/** Re-points the button's colour properties for the dark payload panel. */\nconst onDarkButtonStyle = {\n \"--button-fg\": \"var(--lea-palette-text-contrast)\",\n \"--button-border\": \"var(--lea-palette-border-contrast)\",\n \"--button-tint\": \"var(--lea-color-neutral-800)\",\n} as CSSProperties;\n\nexport type DevPanelPayloadProps = {\n /**\n * The payload to preview. Objects are pretty-printed as JSON; strings are\n * shown verbatim.\n */\n value: unknown;\n /** Label on the toggle. Defaults to `\"Payload preview\"`. */\n label?: ReactNode;\n /** Heading of the expanded dark panel. Defaults to `label`. */\n title?: ReactNode;\n /**\n * Where the payload opens. `\"inline\"` (default) expands a dark panel below the\n * toggle; `\"side\"` opens a floating panel beside the dev panel as a dialog\n * (dismissed with Escape or a click outside); `\"panel\"` has no toggle and\n * shows the payload all the time, filling a `DevPanel.Root` of its own.\n */\n placement?: \"inline\" | \"side\" | \"panel\";\n /** Optional pill in the side flyout header, e.g. `\"live\"`. Ignored inline. */\n badge?: ReactNode;\n /** Controlled open state. Omit for uncontrolled. Ignored by `\"panel\"`. */\n open?: boolean;\n /** Initial open state when uncontrolled. Defaults to `false`. */\n defaultOpen?: boolean;\n /** Called whenever the open state changes. */\n onOpenChange?: (open: boolean) => void;\n /** Accessible name for the copy button. Defaults to a translated label. */\n copyLabel?: string;\n /** Visible label for the side flyout's copy action. Defaults to a translated label. */\n copyJsonLabel?: string;\n /** Accessible name for the side flyout's close button. Defaults to a translated label. */\n closeLabel?: string;\n /**\n * Syntax-highlight JSON payloads. Defaults to `true`. Non-JSON strings always\n * render verbatim. Set `false` for the plain single-colour view.\n */\n highlight?: boolean;\n /**\n * Makes the payload editable: an edit button swaps the JSON for a text box,\n * and Apply calls this with the parsed JSON. Text that is not valid JSON is\n * not applied; the error is shown under the box. Closing the payload, or\n * Escape in the side flyout, throws an unapplied edit away.\n */\n onApply?: (value: unknown) => void;\n /** Called when Reset is pressed, e.g. to drop an applied edit. Omit to hide the button. */\n onReset?: () => void;\n /** Accessible name for the edit button. Defaults to a translated label. */\n editLabel?: string;\n /** Label for the apply button. Defaults to a translated label. */\n applyLabel?: ReactNode;\n /** Label for the cancel button. Defaults to a translated label. */\n cancelLabel?: ReactNode;\n /** Accessible name for the reset button. Defaults to a translated label. */\n resetLabel?: string;\n /** Start of the message for text that is not valid JSON. Defaults to a translated label. */\n invalidJsonLabel?: string;\n};\n\nconst serialise = (value: unknown): string => {\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n};\n\n// Avoid the useLayoutEffect SSR warning while still positioning before paint on\n// the client (the flyout only opens in response to a client interaction).\nconst useIsomorphicLayoutEffect =\n typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\ntype FlyoutRect = { top: number; left: number; width: number; height: number };\n\nconst GAP = 12;\nconst MAX_WIDTH = 420;\nconst MIN_WIDTH = 280;\n\n/**\n * A payload preview. The toggle is a disclosure (`aria-expanded`/`aria-controls`)\n * that reveals the pretty-printed JSON with a copy control; copy success is\n * announced via a `role=\"status\"` live region. With `placement=\"inline\"`\n * (default) it expands a dark panel below the toggle; with `placement=\"side\"` it\n * opens a floating dialog beside the panel (Escape / click-outside to close,\n * focus moves in and returns to the toggle on close); with `placement=\"panel\"`\n * there is no toggle and it is always shown. Uncontrolled by default; drive it\n * with `open`/`onOpenChange` when needed.\n *\n * With `onApply` it can be edited: the text box is labelled by the title, and\n * an invalid edit is marked `aria-invalid` and described by an alert.\n */\nexport function DevPanelPayload({\n value,\n label = \"Payload preview\",\n title,\n placement = \"inline\",\n badge,\n open,\n defaultOpen = false,\n onOpenChange,\n copyLabel,\n copyJsonLabel,\n closeLabel,\n highlight = true,\n onApply,\n onReset,\n editLabel,\n applyLabel,\n cancelLabel,\n resetLabel,\n invalidJsonLabel,\n}: DevPanelPayloadProps) {\n const t = useDevPanelLabels();\n const { copied, copy } = useCopy();\n const isControlled = open !== undefined;\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const isPanel = placement === \"panel\";\n const isOpen = isPanel || (isControlled ? open : internalOpen);\n const panelId = useId();\n const titleId = useId();\n const errorId = useId();\n const isSide = placement === \"side\";\n\n // The text being edited, or `null` while the payload is only shown.\n const [draft, setDraft] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const isEditing = draft !== null;\n const editButtonRef = useRef<HTMLButtonElement>(null);\n const editorRef = useRef<HTMLTextAreaElement>(null);\n // Where focus goes once the editor or the edit button has rendered.\n const pendingFocus = useRef<\"editor\" | \"edit\" | null>(null);\n\n const toggleRef = useRef<HTMLButtonElement>(null);\n const flyoutRef = useRef<HTMLDivElement>(null);\n const [rect, setRect] = useState<FlyoutRect | null>(null);\n // The side flyout is rendered into the body, which is only there in the\n // browser, so the server and the first client render leave it out.\n const [portalTarget, setPortalTarget] = useState<HTMLElement | null>(null);\n useEffect(() => setPortalTarget(document.body), []);\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange],\n );\n\n const text = serialise(value);\n const body =\n highlight && looksLikeJson(value) ? <JsonHighlight code={text} /> : text;\n\n useEffect(() => {\n const target = pendingFocus.current;\n pendingFocus.current = null;\n if (target === \"editor\") editorRef.current?.focus();\n if (target === \"edit\") editButtonRef.current?.focus();\n });\n\n // Closing the payload throws an unapplied edit away.\n useEffect(() => {\n if (isOpen) return;\n setDraft(null);\n setError(null);\n }, [isOpen]);\n\n const startEditing = () => {\n pendingFocus.current = \"editor\";\n setDraft(text);\n setError(null);\n };\n\n const stopEditing = () => {\n pendingFocus.current = \"edit\";\n setDraft(null);\n setError(null);\n };\n\n const apply = () => {\n if (draft === null) return;\n let parsed: unknown;\n try {\n parsed = JSON.parse(draft);\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause));\n return;\n }\n stopEditing();\n onApply?.(parsed);\n };\n\n const position = useCallback(() => {\n const anchor =\n (toggleRef.current?.closest(\n \"[data-dev-panel-root]\",\n ) as HTMLElement | null) ?? toggleRef.current;\n if (!anchor) return;\n const r = anchor.getBoundingClientRect();\n const width = Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, r.width));\n // Prefer the left of the panel; flip to the right when there isn't room.\n let left = r.left - width - GAP;\n if (left < GAP) {\n left = Math.min(r.right + GAP, window.innerWidth - width - GAP);\n }\n const height = Math.min(r.height, window.innerHeight - GAP * 2);\n const top = Math.min(\n Math.max(GAP, r.top),\n window.innerHeight - GAP - height,\n );\n setRect({ top, left, width, height });\n }, []);\n\n useIsomorphicLayoutEffect(() => {\n if (!isSide || !isOpen) return;\n position();\n const onChange = () => position();\n window.addEventListener(\"resize\", onChange);\n window.addEventListener(\"scroll\", onChange, true);\n return () => {\n window.removeEventListener(\"resize\", onChange);\n window.removeEventListener(\"scroll\", onChange, true);\n };\n }, [isSide, isOpen, position]);\n\n // Until the flyout has a place it is hidden, and a hidden element cannot\n // take focus, so the focus handling waits for the first `rect`.\n const isPlaced = rect !== null;\n\n useEffect(() => {\n if (!isSide || !isOpen || !portalTarget || !isPlaced) return;\n const flyout = flyoutRef.current;\n const toggle = toggleRef.current;\n\n const focusable = () =>\n Array.from(\n flyout?.querySelectorAll<HTMLElement>(\n 'button, [href], input, textarea, select, [tabindex]:not([tabindex=\"-1\"])',\n ) ?? [],\n ).filter((el) => !el.hasAttribute(\"disabled\"));\n\n (focusable()[0] ?? flyout)?.focus();\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n setOpen(false);\n return;\n }\n if (event.key !== \"Tab\") return;\n const items = focusable();\n if (items.length === 0) {\n event.preventDefault();\n flyout?.focus();\n return;\n }\n const first = items[0];\n const last = items[items.length - 1];\n const active = document.activeElement;\n if (event.shiftKey && (active === first || active === flyout)) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n }\n };\n\n const onPointerDown = (event: PointerEvent) => {\n const target = event.target as Node;\n if (flyout?.contains(target) || toggle?.contains(target)) return;\n setOpen(false);\n };\n\n document.addEventListener(\"keydown\", onKeyDown, true);\n document.addEventListener(\"pointerdown\", onPointerDown, true);\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown, true);\n document.removeEventListener(\"pointerdown\", onPointerDown, true);\n toggle?.focus();\n };\n }, [isSide, isOpen, portalTarget, isPlaced, setOpen]);\n\n const shownText = draft ?? text;\n\n const copyButton = (\n <IconButton\n aria-label={copyLabel ?? t.copy}\n onClick={() => copy(shownText)}\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n className={styles.OnDark}\n style={onDarkButtonStyle}\n >\n {copied ? <Check size={16} aria-hidden /> : <Copy size={16} aria-hidden />}\n </IconButton>\n );\n\n // Reset and edit, shown while the payload is not being edited.\n const editActions = !isEditing && (\n <>\n {onReset && (\n <IconButton\n aria-label={resetLabel ?? t.reset}\n onClick={onReset}\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n className={styles.OnDark}\n style={onDarkButtonStyle}\n >\n <Undo2 size={16} aria-hidden />\n </IconButton>\n )}\n {onApply && (\n <IconButton\n ref={editButtonRef}\n aria-label={editLabel ?? t.edit}\n onClick={startEditing}\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n className={styles.OnDark}\n style={onDarkButtonStyle}\n >\n <Pencil size={16} aria-hidden />\n </IconButton>\n )}\n </>\n );\n\n // The payload as text, or the box to edit it in with Cancel and Apply.\n const content = isEditing ? (\n <>\n <textarea\n ref={editorRef}\n value={draft}\n onChange={(event) => {\n setDraft(event.target.value);\n setError(null);\n }}\n aria-labelledby={titleId}\n aria-invalid={error !== null ? true : undefined}\n aria-describedby={error !== null ? errorId : undefined}\n spellCheck={false}\n autoCapitalize=\"off\"\n autoComplete=\"off\"\n className={styles.PayloadEditor}\n />\n {error !== null && (\n <p id={errorId} role=\"alert\" className={styles.PayloadError}>\n {`${invalidJsonLabel ?? t.invalidJson}: ${error}`}\n </p>\n )}\n <div className={styles.PayloadActions}>\n <Button\n onClick={stopEditing}\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n className={styles.OnDark}\n style={onDarkButtonStyle}\n >\n {cancelLabel ?? t.cancel}\n </Button>\n <Button onClick={apply} variant=\"solid\" size=\"sm\">\n {applyLabel ?? t.apply}\n </Button>\n </div>\n </>\n ) : (\n <pre tabIndex={0} className={styles.PayloadCode}>\n {body}\n </pre>\n );\n\n return (\n <div className={styles.Payload} data-placement={placement}>\n {!isPanel && (\n <button\n ref={toggleRef}\n type=\"button\"\n aria-expanded={isOpen}\n aria-controls={panelId}\n data-placement={placement}\n onClick={() => setOpen(!isOpen)}\n className={styles.PayloadToggle}\n >\n <span className={styles.PayloadToggleLabel}>\n <Braces size={16} aria-hidden />\n {label}\n </span>\n <span className={styles.PayloadChevron}>\n <ChevronRight size={16} aria-hidden />\n </span>\n </button>\n )}\n\n {!isSide && isOpen && (\n <div\n id={panelId}\n className={styles.PayloadPanel}\n data-placement={placement}\n >\n <div className={styles.PayloadHeader}>\n <span id={titleId}>{title ?? label}</span>\n <span className={styles.HeaderActions}>\n {editActions}\n {copyButton}\n </span>\n </div>\n {content}\n </div>\n )}\n\n {isSide &&\n isOpen &&\n portalTarget &&\n createPortal(\n <div\n ref={flyoutRef}\n id={panelId}\n role=\"dialog\"\n aria-labelledby={titleId}\n tabIndex={-1}\n className={styles.Flyout}\n style={\n rect\n ? {\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n }\n : { visibility: \"hidden\" }\n }\n >\n <div className={styles.PayloadHeader}>\n <span className={styles.FlyoutTitle}>\n <span id={titleId}>{title ?? label}</span>\n {badge != null && (\n <span className={styles.Badge} data-on-dark=\"\">\n {badge}\n </span>\n )}\n </span>\n <span className={styles.HeaderActions}>\n {editActions}\n <IconButton\n aria-label={closeLabel ?? t.close}\n onClick={() => setOpen(false)}\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n className={styles.OnDark}\n style={onDarkButtonStyle}\n >\n <X size={16} aria-hidden />\n </IconButton>\n </span>\n </div>\n {content}\n {!isEditing && (\n <div className={styles.FlyoutFooter}>\n <button\n type=\"button\"\n onClick={() => copy(text)}\n className={styles.FlyoutCopyButton}\n >\n {copied ? (\n <Check size={16} aria-hidden />\n ) : (\n <Copy size={16} aria-hidden />\n )}\n {copied ? t.copied : (copyJsonLabel ?? t.copyJson)}\n </button>\n </div>\n )}\n </div>,\n portalTarget,\n )}\n\n <span role=\"status\" aria-live=\"polite\" className={styles.VisuallyHidden}>\n {copied ? t.copied : \"\"}\n </span>\n </div>\n );\n}\n","\"use client\";\n\nimport { ChevronRight, RotateCcw, Upload } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { Button } from \"../index\";\nimport styles from \"./dev-panel.module.css\";\nimport { useCopy } from \"./use-copy\";\n\nexport type DevPanelFooterProps = {\n /** Called when \"Reseed\" is pressed. Omit to hide the button. */\n onReseed?: () => void;\n /** Label for the reseed button. Defaults to `\"Reseed\"`. */\n reseedLabel?: ReactNode;\n /** Called when \"Copy URL\" is pressed. Omit to hide the button. */\n onCopyUrl?: () => void;\n /** When set, this URL is written to the clipboard before `onCopyUrl` fires. */\n url?: string;\n /** Label for the copy-URL button. Defaults to `\"Copy URL\"`. */\n copyUrlLabel?: ReactNode;\n /** Called when \"Console\" is pressed. Omit to hide the button. */\n onConsole?: () => void;\n /** Label for the console button. Defaults to `\"Console\"`. */\n consoleLabel?: ReactNode;\n /** Extra actions appended after the built-in buttons. */\n children?: ReactNode;\n};\n\n/**\n * The footer action row. Renders a button only for each callback provided, so\n * apps opt into just the actions they support. `Copy URL` writes `url` to the\n * clipboard (when given) before invoking `onCopyUrl`. Extra `children` are\n * appended after the built-ins.\n */\nexport function DevPanelFooter({\n onReseed,\n reseedLabel = \"Reseed\",\n onCopyUrl,\n url,\n copyUrlLabel = \"Copy URL\",\n onConsole,\n consoleLabel = \"Console\",\n children,\n}: DevPanelFooterProps) {\n const { copy } = useCopy();\n\n const handleCopyUrl = async () => {\n if (url != null) await copy(url);\n onCopyUrl?.();\n };\n\n return (\n <div className={styles.Footer}>\n {onReseed && (\n <Button\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n startIcon={<RotateCcw aria-hidden />}\n onClick={onReseed}\n >\n {reseedLabel}\n </Button>\n )}\n {onCopyUrl && (\n <Button\n variant=\"outline\"\n color=\"secondary\"\n size=\"sm\"\n startIcon={<Upload aria-hidden />}\n onClick={handleCopyUrl}\n >\n {copyUrlLabel}\n </Button>\n )}\n {onConsole && (\n <Button\n variant=\"solid\"\n size=\"sm\"\n endIcon={<ChevronRight aria-hidden />}\n onClick={onConsole}\n style={{ marginInlineStart: \"auto\" }}\n >\n {consoleLabel}\n </Button>\n )}\n {children}\n </div>\n );\n}\n","/*\n * The members of the `DevPanel` namespace. The parts live in files of their\n * own under their full names (`DevPanelStatus` in dev-panel-status.tsx);\n * here each gets the short name it has in the namespace (`DevPanel.Status`).\n */\nexport {\n DevPanel as Root,\n DevPanelHandle as Handle,\n DevPanelSection as Section,\n useDevPanelSectionLabelId as useSectionLabelId,\n type DevPanelHandleProps as HandleProps,\n type DevPanelPosition as Position,\n type DevPanelProps as RootProps,\n type DevPanelSectionProps as SectionProps,\n} from \"./dev-panel\";\nexport {\n DevPanelCountry as Country,\n type DevPanelCountryOption as CountryOption,\n type DevPanelCountryProps as CountryProps,\n} from \"./dev-panel-country\";\nexport {\n DevPanelVariant as Variant,\n type DevPanelVariantOption as VariantOption,\n type DevPanelVariantProps as VariantProps,\n} from \"./dev-panel-variant\";\nexport {\n DevPanelStepState as StepState,\n type DevPanelStepStateOption as StepStateOption,\n type DevPanelStepStateProps as StepStateProps,\n} from \"./dev-panel-step-state\";\nexport {\n DevPanelSteps as Steps,\n type DevPanelStep as Step,\n type DevPanelStepsProps as StepsProps,\n} from \"./dev-panel-steps\";\nexport {\n DevPanelStatus as Status,\n type DevPanelStatusColor as StatusColor,\n type DevPanelStatusOption as StatusOption,\n type DevPanelStatusProps as StatusProps,\n} from \"./dev-panel-status\";\nexport {\n DevPanelFlow as Flow,\n type DevPanelFlowProps as FlowProps,\n} from \"./dev-panel-flow\";\nexport {\n DevPanelPayload as Payload,\n type DevPanelPayloadProps as PayloadProps,\n} from \"./dev-panel-payload\";\nexport {\n DevPanelFooter as Footer,\n type DevPanelFooterProps as FooterProps,\n} from \"./dev-panel-footer\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GCQa,KAAoB,EAAkB;CACjD,MAAM;EAAE,IAAI;EAAU,IAAI;EAAW,IAAI;EAAU,IAAI;CAAS;CAChE,QAAQ;EAAE,IAAI;EAAW,IAAI;EAAY,IAAI;EAAY,IAAI;CAAU;CACvE,UAAU;EACR,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN;CACA,OAAO;EAAE,IAAI;EAAQ,IAAI;EAAS,IAAI;EAAS,IAAI;CAAS;CAC5D,MAAM;EAAE,IAAI;EAAW,IAAI;EAAY,IAAI;EAAW,IAAI;CAAS;CACnE,OAAO;EAAE,IAAI;EAAQ,IAAI;EAAY,IAAI;EAAS,IAAI;CAAU;CAChE,QAAQ;EAAE,IAAI;EAAU,IAAI;EAAU,IAAI;EAAW,IAAI;CAAW;CACpE,OAAO;EACL,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN;CACA,aAAa;EACX,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN;CACA,UAAU;EACR,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN;CACA,QAAQ;EAAE,IAAI;EAAS,IAAI;EAAa,IAAI;EAAY,IAAI;CAAW;CACvE,MAAM;EAAE,IAAI;EAAS,IAAI;EAAQ,IAAI;EAAW,IAAI;CAAU;CAC9D,MAAM;EAAE,IAAI;EAAO,IAAI;EAAQ,IAAI;EAAS,IAAI;CAAU;CAC1D,KAAK;EAAE,IAAI;EAAQ,IAAI;EAAQ,IAAI;EAAY,IAAI;CAAQ;CAC3D,MAAM;EAAE,IAAI;EAAQ,IAAI;EAAQ,IAAI;EAAS,IAAI;CAAO;CACxD,iBAAiB;EACf,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN;CACA,eAAe;EACb,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN;CACA,gBAAgB;EACd,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN;CACA,gBAAgB;EACd,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN;AACF,CAAC,GCjDK,MAAQ,GAAG,MACf,EAAM,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,GAO1B,KAAyB,EAAkC,KAAA,CAAS;AAG1E,SAAgB,IAA4B;CAC1C,OAAO,EAAW,EAAsB;AAC1C;AAWA,IAAM,KAAsB,EAC1B,IACF,GAGM,IAAgB,yBAEhB,UACJ,OAAO,YAAc,OACrB,mBAAmB,KAAK,UAAU,YAAY,UAAU,SAAS;AAOnE,SAAgB,GACd,GACA,GACS;CACT,IAAM,IAAQ,EAAS,MAAM,GAAG,CAAC,CAAC,KAAK,MAAS,EAAK,KAAK,CAAC,CAAC,YAAY,CAAC,GACnE,IAAM,EAAM,IAAI;CACtB,IAAI,CAAC,GAAK,OAAO;CAEjB,IAAM,IAAM,EAAM,SAAS,KAAK,GAC1B,IAAQ;EACZ,MAAM,EAAM,SAAS,MAAM,KAAM,KAAO,CAAC,EAAM;EAC/C,MAAM,EAAM,SAAS,MAAM,KAAM,KAAO,EAAM;EAC9C,KAAK,EAAM,SAAS,KAAK;EACzB,OAAO,EAAM,SAAS,OAAO;CAC/B;CAUA,OARE,EAAM,YAAY,EAAM,QACxB,EAAM,YAAY,EAAM,QACxB,EAAM,WAAW,EAAM,OACvB,EAAM,aAAa,EAAM,QAElB,KAIP,EAAM,IAAI,YAAY,MAAM,KAC5B,EAAM,KAAK,YAAY,MAAM,MAAM,OACnC,EAAM,KAAK,YAAY,MAAM,QAAQ;AAEzC;AAoGA,SAAgB,EAAS,EACvB,WAAQ,gBACR,UACA,mBAAgB,IAChB,iBAAc,IACd,cACA,qBACA,sBACA,kBACA,iBACA,WACA,kBACA,mBACA,cAAW,MAAW,KAAA,KAAa,MAAkB,KAAA,GACrD,cAAW,eACX,YACA,cACA,aACA,WACA,kBACA,oBACA,cAAW,MAAW,KAAA,KAAa,MAAkB,KAAA,GACrD,cACA,cACA,cACA,YACgB;CAChB,IAAM,IAAI,GAAkB,GACtB,IAAe,MAAc,KAAA,GAC7B,CAAC,IAAmB,KAAwB,EAChD,KAAoB,EACtB,GACM,IAAc,IAAe,IAAY,IAEzC,CAAC,GAAgB,MAAqB,EAAS,KAAiB,EAAK,GACrE,IAAW,MAAa,KAAU,IAElC,CAAC,GAAgB,KAAqB,EAAS,KAAiB,EAAK,GACrE,IAAW,KAAU,GACrB,IAAS,KAAY,MAAa,KAAA,GAElC,IAAU,EAAuB,IAAI,GACrC,IAAY,EAA0B,IAAI,GAC1C,IAAgB,EAA0B,IAAI,GAE9C,IAAe,EAAkC,IAAI,GAErD,UAAwB;EAC5B,IAAM,IAAO,CAAC;EAEd,AADK,KAAc,EAAqB,CAAI,GAC5C,IAAoB,CAAI;CAC1B,GAEM,UAAqB;EACzB,IAAM,IAAO,CAAC;EAEd,AADI,MAAW,KAAA,KAAW,EAAkB,CAAI,GAChD,KAAiB,CAAI;CACvB,GAEM,KAAkB,MAAkB;EAExC,AADI,MAAW,KAAA,KAAW,GAAkB,CAAI,GAChD,IAAiB,CAAI;CACvB;CAeA,AAZA,QAAgB;EACd,AACE,KACA,CAAC,KACD,MAAqB,KAAA,KACrB,OAAO,SAAW,OAClB,OAAO,aAAa,CAAa,CAAC,CAAC,WAEnC,EAAqB,EAAI;CAE7B,GAAG,CAAC,CAAC,GAEL,QAAgB;EACd,IAAM,IAAS,EAAa;EAG5B,AAFA,EAAa,UAAU,MACnB,MAAW,YAAU,EAAU,SAAS,MAAM,GAC9C,MAAW,WAAS,EAAc,SAAS,MAAM;CACvD,CAAC;CAGD,IAAM,KAAS,EAAO;EAAE;EAAU;CAAe,CAAC;CAGlD,AAFA,GAAO,UAAU;EAAE;EAAU;CAAe,GAE5C,QAAgB;EACd,IAAI,CAAC,KAAY,MAAa,IAAO;EACrC,IAAM,KAAa,MAAyB;GAC1C,IAAI,EAAM,UAAU,CAAC,GAAgB,GAAO,CAAQ,GAAG;GACvD,EAAM,eAAe;GACrB,IAAM,EAAE,UAAU,GAAW,gBAAgB,MAAQ,GAAO,SACtD,IAAS,SAAS;GAMxB,AAJI,CAAC,KAAa,EAAQ,SAAS,SAAS,CAAM,MAChD,EAAa,UAAU,WACrB,KAAa,KAAU,QAAQ,MAAW,EAAU,YACtD,EAAa,UAAU,UACzB,EAAI,CAAC,CAAS;EAChB;EAEA,OADA,OAAO,iBAAiB,WAAW,CAAS,SAC/B,OAAO,oBAAoB,WAAW,CAAS;CAC9D,GAAG,CAAC,GAAU,CAAQ,CAAC;CAEvB,IAAM,KAAS,EAAM,GACf,IACJ,MAAa,KAAA,IACT,KAAA,IACA,EAAS,SAAS,MAAM,IACtB,SACA;CAoBR,OAlBI,IAEA,kBAAC,GAAoB,UAArB;EACE,OAAO;GACL,YAAY;IAEV,AADA,EAAa,UAAU,SACvB,EAAe,EAAK;GACtB;GACA;GACA;GACA;EACF;EAEC,UAAA,MAAU,kBAAC,IAAD,CAAiB,CAAA;CACA,CAAA,IAKhC,kBAAC,OAAD;EACE,KAAK;EACL,uBAAoB;EACpB,iBAAe;EACf,iBAAe,IAAW,KAAK,KAAA;EAC/B,kBAAgB,IAAc,KAAK,KAAA;EAEnC,eAAa,KAAU,KAAY,CAAC,IAAc,KAAK,KAAA;EACvD,WAAW,GAAK,EAAO,UAAU,CAAS;EACnC;EATT,UAAA,CAaE,kBAAC,OAAD;GAAK,WAAW,EAAO;GAAvB,UAAA,CACE,kBAAC,QAAD;IAAM,WAAW,EAAO;IAAxB,UAAA;KACG,KAAiB,kBAAC,QAAD;MAAM,WAAW,EAAO;MAAW,eAAA;KAAa,CAAA;KAClE,kBAAC,QAAD,EAAA,UAAO,EAAY,CAAA;KAClB,KAAS,QAAQ,kBAAC,QAAD;MAAM,WAAW,EAAO;MAAQ,UAAA;KAAY,CAAA;IAC1D;GACN,CAAA,GAAA,kBAAC,QAAD;IAAM,WAAW,EAAO;IAAxB,UAAA;KACG,KAAU,CAAC,KACV,kBAAC,GAAD;MACE,WAAW,EAAO;MAClB,cAAY,MAAY,EAAE;MAC1B,gBAAc;MACd,SAAS;MACT,SAAS,IAAW,UAAU;MAC9B,OAAM;MACN,MAAK;MAEL,UAAA,kBAAC,GAAD;OAAK,MAAM;OAAI,eAAA;MAAa,CAAA;KAClB,CAAA;KAEb,KACC,kBAAC,GAAD;MACE,KAAK;MACL,WAAW,EAAO;MAClB,cAAY,KAAa,EAAE;MAC3B,eAAe;OAEb,AADA,EAAa,UAAU,UACvB,EAAe,EAAI;MACrB;MACA,SAAQ;MACR,OAAM;MACN,MAAK;MAEL,UAAA,kBAAC,GAAD;OAAQ,MAAM;OAAI,eAAA;MAAa,CAAA;KACrB,CAAA;KAEb,KACC,kBAAC,GAAD;MACE,cACE,IACK,MAAe,EAAE,SACjB,KAAiB,EAAE;MAE1B,iBAAe,CAAC;MAChB,iBAAe;MACf,SAAS;MACT,SAAQ;MACR,OAAM;MACN,MAAK;MAEJ,UACC,EADD,IACE,IAEA,GAFD;OAAM,MAAM;OAAI,eAAA;MAAa,CAEC;KAEtB,CAAA;IAEV;GACH,CAAA,CAAA;EACJ,CAAA,GAAA,CAAC,KACA,kBAAC,OAAD;GAAK,IAAI;GAAQ,WAAW,EAAO;GAChC;EACE,CAAA,CAEJ;;AAET;AAqBA,SAAgB,EAAgB,EAC9B,UACA,SACA,aACA,cACA,YACuB;CACvB,IAAM,IAAU,EAAM;CAEtB,OACE,kBAAC,WAAD;EACE,mBAAiB;EACjB,WAAW,GAAK,EAAO,SAAS,CAAS;EAClC;EAHT,UAAA,CAKE,kBAAC,OAAD;GAAK,WAAW,EAAO;GAAvB,UAAA,CACE,kBAAC,QAAD;IAAM,IAAI;IAAS,WAAW,EAAO;IAClC,UAAA;GACG,CAAA,GACL,KAAQ,QAAQ,kBAAC,QAAD;IAAM,WAAW,EAAO;IAAc,UAAA;GAAW,CAAA,CAC/D;EACL,CAAA,GAAA,kBAAC,GAAuB,UAAxB;GAAiC,OAAO;GACrC;EAC8B,CAAA,CAC1B;;AAEb;AA0BA,SAAgB,GAAe,EAC7B,MAAM,GACN,UACA,cACA,YACsB;CACtB,IAAM,IAAI,GAAkB,GACtB,IAAO,EAAW,EAAmB;CAC3C,IAAI,CAAC,GAAM,OAAO;CAElB,IAAM,IAAO,KAAS,EAAK,OACrB,IAAO,KAAY,EAAK,QAAQ;CAEtC,OACE,kBAAC,UAAD;EACE,KAAK,EAAK;EACV,MAAK;EACL,WAAW,GAAK,EAAO,QAAQ,CAAS;EACjC;EACP,aAAW;EAEX,cAAY,OAAO,KAAS,WAAW,GAAG,EAAE,KAAK,GAAG,MAAS,KAAA;EAC7D,SAAS,EAAK;EARhB,UAAA,CAUE,kBAAC,QAAD;GAAM,WAAW,EAAO;GAAW,eAAA;EAAa,CAAA,GAChD,kBAAC,QAAD,EAAA,UAAO,EAAW,CAAA,CACZ;;AAEZ;;;AC5cA,SAAgB,GAAkB,EAChC,WACA,aACA,aACA,iBAC2B;CAC3B,IAAM,IAAO,EAA+B,CAAC,CAAC,GAExC,IAAa,GAChB,MAAkB,IAAa,EAAO,IAAQ,CAAK,KAAK,IACzD,CAAC,GAAY,CAAM,CACrB,GAEM,IAAgB,EAAO,WAAW,MAAU,MAAU,CAAQ,GAC9D,IAAoB,EAAO,WAAW,GAAG,MAAU,CAAC,EAAW,CAAK,CAAC,GAGrE,IACJ,KAAiB,KAAK,CAAC,EAAW,CAAa,IAC3C,IACA,GAEA,IAAiB,GACpB,MAAkB;EAEjB,AADA,EAAK,QAAQ,EAAM,EAAE,MAAM,GAC3B,EAAS,EAAO,EAAM;CACxB,GACA,CAAC,GAAU,CAAM,CACnB,GAEM,IAAc,GACjB,GAAc,MAAsB;EACnC,IAAM,IAAQ,EAAO;EACrB,KAAK,IAAI,IAAO,GAAG,KAAQ,GAAO,KAAQ,GAAG;GAC3C,IAAM,KAAS,IAAO,IAAY,IAAO,IAAQ,KAAQ;GACzD,IAAI,CAAC,EAAW,CAAK,GAAG,OAAO;EACjC;EACA,OAAO;CACT,GACA,CAAC,GAAY,EAAO,MAAM,CAC5B;CA4DA,OAAO,EAAE,cA1DY,GAClB,MAAwC;EACvC,IAAM,IAAQ,EAAO,IACf,IAAW,EAAW,CAAK;EAEjC,OAAO;GACL,MAAM,MAAgC;IACpC,EAAK,QAAQ,KAAS;GACxB;GACA,MAAM;GACN,gBAAgB,MAAU;GAC1B,iBAAiB,KAAY,KAAA;GAC7B,UAAU,MAAU,IAAgB,IAAI;GACxC,eAAe;IACb,AAAK,KAAU,EAAS,CAAK;GAC/B;GACA,YAAY,MAAsC;IAChD,QAAQ,EAAM,KAAd;KACE,KAAK;KACL,KAAK;MAEH,AADA,EAAM,eAAe,GACrB,EAAe,EAAY,GAAO,CAAC,CAAC;MACpC;KACF,KAAK;KACL,KAAK;MAEH,AADA,EAAM,eAAe,GACrB,EAAe,EAAY,GAAO,EAAE,CAAC;MACrC;KACF,KAAK;MAEH,AADA,EAAM,eAAe,GACrB,EAAe,EAAY,IAAI,CAAC,CAAC;MACjC;KACF,KAAK;MAEH,AADA,EAAM,eAAe,GACrB,EAAe,EAAY,GAAG,EAAE,CAAC;MACjC;KACF,KAAK;KACL,KAAK,SAEH,AADA,EAAM,eAAe,GAChB,KAAU,EAAS,CAAK;IAIjC;GACF;EACF;CACF,GACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAGO,EAAa;AACxB;;;ACjGA,SAAgB,EAAsC,EACpD,UACA,aACA,YACA,aAAU,QACV,cAAc,GACd,mBAAmB,KACW;CAC9B,IAAM,IAAiB,EAA0B,GAC3C,IAAa,MAAmB,IAAY,KAAA,IAAY,IAExD,EAAE,oBAAiB,GAAkB;EACzC,QAAQ,EAAQ,KAAK,MAAW,EAAO,KAAK;EAC5C,UAAU;EACV,UAAU;EACV,aAAa,GAAG,MAAU,EAAQ,EAAM,EAAE,YAAY;CACxD,CAAC,GAEK,IAAS,MAAY;CAE3B,OACE,kBAAC,OAAD;EACE,MAAK;EACL,cAAY;EACZ,mBAAiB;EACjB,WAAW,IAAS,EAAO,YAAY,EAAO;EAE7C,UAAA,EAAQ,KAAK,GAAQ,MAAU;GAE9B,IAAM,EAAE,cAAW,GAAG,MAAc,EAAa,CAAK;GACtD,OACE,kBAAC,UAAD;IAEE,MAAK;IACL,iBAAe,EAAO,WAAW,KAAK,KAAA;IAC3B;IACX,GAAI;IACJ,WAAW,IAAS,EAAO,OAAO,EAAO;IAN3C,UAAA,CAQG,IACC,kBAAC,QAAD;KAAM,WAAW,EAAO;KAAS,eAAA;IAAa,CAAA,IAE9C,EAAO,MAET,kBAAC,QAAD,EAAA,UAAO,EAAO,MAAY,CAAA,CACpB;GAbD,GAAA,EAAO,KAaN;EAEZ,CAAC;CACE,CAAA;AAET;;;AC5DA,SAAgB,EAAkC,EAChD,UACA,aACA,YACA,WAAQ,aACkB;CAC1B,IAAM,IAAyC,EAAQ,KAAK,OAAY;EACtE,OAAO,EAAO;EACd,UAAU,EAAO;EACjB,OAAO,EAAO;EACd,MACE,EAAO,QAAQ,OAEX,KAAA,IADF,kBAAC,QAAD;GAAM,WAAW,EAAO;GAAc,UAAA,EAAO;EAAW,CAAA;CAE9D,EAAE;CAEF,OACE,kBAAC,GAAD;EAAwB;EACtB,UAAA,kBAAC,GAAD;GACS;GACG;GACV,SAAS;EACV,CAAA;CACc,CAAA;AAErB;;;AC/BA,SAAgB,EAAkC,EAChD,UACA,aACA,YACA,WAAQ,aACkB;CAC1B,IAAM,IAAyC,EAAQ,KAAK,OAAY;EACtE,OAAO,EAAO;EACd,OAAO,EAAO;EACd,UAAU,EAAO;CACnB,EAAE;CAEF,OACE,kBAAC,GAAD;EAAwB;EACtB,UAAA,kBAAC,GAAD;GACS;GACG;GACV,SAAS;EACV,CAAA;CACc,CAAA;AAErB;;;ACZA,SAAgB,GAAoC,EAClD,UACA,aACA,YACA,WAAQ,cACR,cACA,qBAAkB,oBACU;CAC5B,IAAM,IAAyC,EAAQ,KAAK,OAAY;EACtE,OAAO,EAAO;EACd,OAAO,EAAO;EACd,UAAU,EAAO;CACnB,EAAE;CAEF,OACE,kBAAC,GAAD;EAAwB;EAAxB,UAAA,CACG,KAAa,QACZ,kBAAC,GAAD;GACE,WAAU;GACV,OAAO;IACL,OAAO;IACP,gBAAgB;GAClB;GALF,UAAA;IAOG;IAAiB;IAClB,kBAAC,QAAD;KAAM,WAAW,EAAO;KAAgB,UAAA;IAAgB,CAAA;GACpD;EAER,CAAA,GAAA,kBAAC,GAAD;GACS;GACG;GACV,SAAS;GACT,SAAQ;EACT,CAAA,CACc;;AAErB;;;AC/BA,IAAM,KACJ,GACA,MACG;CACH,QAAQ,GAAR;EACE,KAAK,YACH,OAAO,EAAE;EACX,KAAK,WACH,OAAO,EAAE;EACX,SACE,OAAO,EAAE;CACb;AACF;AAQA,SAAgB,EAAc,EAC5B,UACA,gBACA,iBACA,WAAQ,gBACR,eAAY,IACZ,aAAU,KACW;CACrB,IAAM,IAAI,GAAkB,GACtB,IAAQ,EAAM,QACd,IACJ,KAAa,KAAe,OAAO,GAAG,EAAY,KAAK,MAAU,KAAA;CAEnE,OACE,kBAAC,GAAD;EAAwB;EAAa;EACnC,UAAA,kBAAC,GAAD;GACS;GACM;GACC;GACL;GACN;EACJ,CAAA;CACc,CAAA;AAErB;AAEA,SAAS,EAAS,EAChB,UACA,gBACA,iBACA,YACA,QAOC;CACD,IAAM,IAAe,EAA0B;CAE/C,OACE,kBAAC,OAAD;EACE,MAAK;EACL,mBAAiB;EACjB,WAAW,EAAO;EAClB,OAAO,EAAE,cAAc,EAAQ;EAE9B,UAAA,EAAM,KAAK,GAAM,MAAU;GAC1B,IAAM,IAAa,IAAQ,GACrB,IACJ,EAAK,WACJ,KAAe,OACZ,aACA,IAAa,IACX,aACA,MAAe,IACb,YACA,aACJ,IAAY,MAAU,WACtB,IAAa,MAAU;GAQ7B,OACE,kBAAC,UAAD;IAEE,MAAK;IACL,gBAAc,IAAY,SAAS,KAAA;IACnC,cAAY;IACZ,iBAAe,EAAK,WAAW,KAAK,KAAA;IACpC,UAAU,EAAK;IACf,eAAe,IAAe,GAAY,CAAI;IAC9C,WAAW,EAAO;IARpB,UAAA;KAUE,kBAAC,QAAD;MAAM,WAAW,EAAO;MAAxB,UAAA;OACE,kBAAC,QAAD;QAAM,WAAW,EAAO;QAErB,UAAA,IAAa,GAAG,EAAE,KAAK,GAAG,MAAe,GAAG,EAAE,KAAK;OAChD,CAAA;OACL,IAAa,kBAAC,GAAD;QAAO,MAAM;QAAI,eAAA;OAAa,CAAA,IAAI;OAC/C,EAAK,WACJ,kBAAC,QAAD;QAAM,WAAW,EAAO;QAAa,eAAA;OAAa,CAAA;MAEhD;;KAAE;KACR,kBAAC,QAAD;MAAM,WAAW,EAAO;MAAY,UAAA,EAAK;KAAY,CAAA;KACrD,kBAAC,QAAD;MAAM,WAAW,EAAO;MAGrB,UAAA,IAAI,EAAW,GAAO,CAAC,IACtB,EAAK,UAAU,KAAK,EAAE,mBAAmB,GAC1C;KACG,CAAA;IACA;GA3BD,GAAA,CA2BC;EAEZ,CAAC;CACE,CAAA;AAET;;;ACxHA,SAAgB,EAAiC,EAC/C,UACA,aACA,YACA,WAAQ,wBACiB;CACzB,OACE,kBAAC,GAAD;EAAwB;EACtB,UAAA,kBAAC,GAAD;GAAmB;GAAiB;GAAmB;EAAU,CAAA;CAClD,CAAA;AAErB;AAEA,SAAS,EAA6B,EACpC,UACA,aACA,cAKC;CACD,IAAM,IAAe,EAA0B,GACzC,EAAE,oBAAiB,GAAkB;EACzC,QAAQ,EAAQ,KAAK,MAAW,EAAO,KAAK;EAC5C,UAAU;EACV,UAAU;EACV,aAAa,GAAG,MAAU,EAAQ,EAAM,EAAE,YAAY;CACxD,CAAC;CAED,OACE,kBAAC,OAAD;EACE,MAAK;EACL,mBAAiB;EACjB,WAAW,EAAO;EAEjB,UAAA,EAAQ,KAAK,GAAQ,MAAU;GAE9B,IAAM,EAAE,cAAW,GAAG,MAAc,EAAa,CAAK;GACtD,OACE,kBAAC,UAAD;IAEE,MAAK;IACL,cAAY,EAAO,SAAS;IAC5B,iBAAe,EAAO,WAAW,KAAK,KAAA;IAC3B;IACX,GAAI;IACJ,WAAW,EAAO;IAPpB,UAAA,CASE,kBAAC,QAAD;KAAM,WAAW,EAAO;KAAW,eAAA;IAAa,CAAA,GAChD,kBAAC,QAAD,EAAA,UAAO,EAAO,MAAY,CAAA,CACpB;GAVD,GAAA,EAAO,KAUN;EAEZ,CAAC;CACE,CAAA;AAET;;;AC5FA,SAAgB,IAAU;CACxB,IAAM,CAAC,GAAQ,KAAa,EAAS,EAAK,GACpC,IAAa,EACjB,KAAA,CACF,GAEM,IAAO,EAAY,OAAO,MAAiB;EAC/C,IAAI;GACF,MAAM,UAAU,WAAW,UAAU,CAAI;EAC3C,QAAQ,CAGR;EAGA,AAFA,EAAU,EAAI,GACd,aAAa,EAAW,OAAO,GAC/B,EAAW,UAAU,iBAAiB,EAAU,EAAK,GAAG,GAAI;CAC9D,GAAG,CAAC,CAAC;CAIL,OAFA,cAAsB,aAAa,EAAW,OAAO,GAAG,CAAC,CAAC,GAEnD;EAAE;EAAQ;CAAK;AACxB;;;ACEA,IAAM,KAAgB,MACpB,OAAO,KAAS,YAAY,OAAO,KAAS,WAAW,OAAO,CAAI,IAAI;AAOxE,SAAgB,EAAa,EAC3B,WACA,YACA,YACA,WAAQ,QACR,cAAW,IACX,cACA,cACA,aACoB;CACpB,IAAM,IAAI,GAAkB,GACtB,EAAE,WAAQ,aAAS,EAAQ,GAE3B,IAAQ;EAAC;EAAQ;EAAS;CAAO,CAAC,CAAC,QACtC,MAA4B,KAAQ,IACvC,GACM,IAAc,EACjB,IAAI,CAAY,CAAC,CACjB,QAAQ,MAAyB,KAAQ,IAAI,CAAC,CAC9C,KAAK,KAAK,GACP,IAAa,KAAa;CAOhC,OACE,kBAAC,OAAD;EAAK,WAAW,EAAO;EAAvB,UAAA;GACE,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAvB,UAAA,CACE,kBAAC,QAAD;KAAM,WAAW,EAAO;KAAY,UAAA;IAAY,CAAA,GAChD,kBAAC,QAAD,EAAA,UACG,EAAM,KAAK,GAAM,MAChB,kBAAC,GAAD,EAAA,UAAA,CACG,IAAQ,KACP,kBAAC,QAAD;KAAM,eAAA;KAAY,WAAW,EAAO;KAAe,UAAA;IAE7C,CAAA,GAEP,CACO,EAAA,GAPK,CAOL,CACX,EACG,CAAA,CACH;;GACJ,KACC,kBAAC,GAAD;IACE,cAAY,KAAa,EAAE;IAC3B,SAAS,YAzBc;KAE7B,AADA,MAAM,GAAK,CAAU,GACrB,IAAS;IACX;IAuBQ,SAAQ;IACR,OAAM;IACN,MAAK;IAEJ,UACC,EADD,IACE,IAEA,GAFD;KAAO,MAAM;KAAI,eAAA;IAAa,CAED;GAErB,CAAA;GAEd,kBAAC,QAAD;IAAM,MAAK;IAAS,aAAU;IAAS,WAAW,EAAO;IACtD,UAAA,IAAS,EAAE,SAAS;GACjB,CAAA;EACH;;AAET;;;ACpFA,IAAM,IACJ,+EAEI,KAAW,MACf,MAAS,OAAO,MAAS,OAAQ,MAAS,QAAQ,MAAS;AAE7D,SAAS,EAAS,GAAa,GAAc,GAA4B;CACvE,IAAI,EAAI,OAAO,MAAK;EAElB,IAAI,IAAI;EACR,OAAO,IAAI,EAAK,UAAU,EAAQ,EAAK,EAAE,IAAG,KAAK;EACjD,OAAO,EAAK,OAAO,MAAM,QAAQ;CACnC;CAGA,OAFI,MAAQ,UAAU,MAAQ,UAAgB,YAC1C,MAAQ,SAAe,SACpB;AACT;AAEA,SAAS,EAAS,GAAuB;CACvC,IAAM,IAAkB,CAAC,GACrB,IAAY;CAEhB,KAAK,IAAM,KAAS,EAAK,SAAS,CAAK,GAAG;EACxC,IAAM,IAAQ,EAAM,SAAS;EAC7B,AAAI,IAAQ,KACV,EAAO,KAAK;GAAE,MAAM;GAAe,OAAO,EAAK,MAAM,GAAW,CAAK;EAAE,CAAC;EAE1E,IAAM,IAAM,EAAM;EAElB,AADA,EAAO,KAAK;GAAE,MAAM,EAAS,GAAK,GAAM,IAAQ,EAAI,MAAM;GAAG,OAAO;EAAI,CAAC,GACzE,IAAY,IAAQ,EAAI;CAC1B;CAMA,OAJI,IAAY,EAAK,UACnB,EAAO,KAAK;EAAE,MAAM;EAAe,OAAO,EAAK,MAAM,CAAS;CAAE,CAAC,GAG5D;AACT;AAOA,SAAgB,GAAc,GAAyB;CACrD,IAAI,OAAO,KAAU,UAAU,OAAO,KAAS;CAC/C,IAAM,IAAU,EAAM,KAAK;CAC3B,IAAI,CAAC,GAAS,OAAO;CACrB,IAAI;EAEF,OADA,KAAK,MAAM,CAAO,GACX;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAQA,SAAgB,GAAc,EAAE,WAA0B;CACxD,IAAM,IAAS,SAAc,EAAS,CAAI,GAAG,CAAC,CAAI,CAAC;CAEnD,OACE,kBAAA,GAAA,EAAA,UACG,EAAO,KAAK,GAAO,MAClB,kBAAC,QAAD;EAAkB,WAAW,EAAO;EAAO,cAAY,EAAM;EAC1D,UAAA,EAAM;CACH,GAFK,CAEL,CACP,EACD,CAAA;AAEN;;;AChEA,IAAM,IAAoB;CACxB,eAAe;CACf,mBAAmB;CACnB,iBAAiB;AACnB,GA2DM,MAAa,MAA2B;CAC5C,IAAI,OAAO,KAAU,UAAU,OAAO;CACtC,IAAI;EACF,OAAO,KAAK,UAAU,GAAO,MAAM,CAAC;CACtC,QAAQ;EACN,OAAO,OAAO,CAAK;CACrB;AACF,GAIM,KACJ,OAAO,SAAW,MAAc,IAAkB,GAI9C,IAAM,IACN,KAAY,KACZ,KAAY;AAelB,SAAgB,EAAgB,EAC9B,UACA,WAAQ,mBACR,UACA,eAAY,UACZ,UACA,SACA,iBAAc,IACd,iBACA,cACA,kBACA,gBACA,eAAY,IACZ,YACA,YACA,eACA,gBACA,iBACA,eACA,wBACuB;CACvB,IAAM,IAAI,GAAkB,GACtB,EAAE,WAAQ,aAAS,EAAQ,GAC3B,IAAe,MAAS,KAAA,GACxB,CAAC,GAAc,MAAmB,EAAS,CAAW,GACtD,KAAU,MAAc,SACxB,IAAS,OAAY,IAAe,IAAO,IAC3C,IAAU,EAAM,GAChB,IAAU,EAAM,GAChB,KAAU,EAAM,GAChB,IAAS,MAAc,QAGvB,CAAC,GAAO,KAAY,EAAwB,IAAI,GAChD,CAAC,GAAO,KAAY,EAAwB,IAAI,GAChD,IAAY,MAAU,MACtB,IAAgB,EAA0B,IAAI,GAC9C,IAAY,EAA4B,IAAI,GAE5C,IAAe,EAAiC,IAAI,GAEpD,IAAY,EAA0B,IAAI,GAC1C,IAAY,EAAuB,IAAI,GACvC,CAAC,GAAM,MAAW,EAA4B,IAAI,GAGlD,CAAC,IAAc,MAAmB,EAA6B,IAAI;CACzE,QAAgB,GAAgB,SAAS,IAAI,GAAG,CAAC,CAAC;CAElD,IAAM,KAAU,GACb,MAAkB;EAEjB,AADK,KAAc,GAAgB,CAAI,GACvC,IAAe,CAAI;CACrB,GACA,CAAC,GAAc,CAAY,CAC7B,GAEM,IAAO,GAAU,CAAK,GACtB,KACJ,KAAa,GAAc,CAAK,IAAI,kBAAC,IAAD,EAAe,MAAM,EAAO,CAAA,IAAI;CAUtE,AARA,QAAgB;EACd,IAAM,IAAS,EAAa;EAG5B,AAFA,EAAa,UAAU,MACnB,MAAW,YAAU,EAAU,SAAS,MAAM,GAC9C,MAAW,UAAQ,EAAc,SAAS,MAAM;CACtD,CAAC,GAGD,QAAgB;EACV,MACJ,EAAS,IAAI,GACb,EAAS,IAAI;CACf,GAAG,CAAC,CAAM,CAAC;CAEX,IAAM,WAAqB;EAGzB,AAFA,EAAa,UAAU,UACvB,EAAS,CAAI,GACb,EAAS,IAAI;CACf,GAEM,WAAoB;EAGxB,AAFA,EAAa,UAAU,QACvB,EAAS,IAAI,GACb,EAAS,IAAI;CACf,GAEM,WAAc;EAClB,IAAI,MAAU,MAAM;EACpB,IAAI;EACJ,IAAI;GACF,IAAS,KAAK,MAAM,CAAK;EAC3B,SAAS,GAAO;GACd,EAAS,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK,CAAC;GAC/D;EACF;EAEA,AADA,GAAY,GACZ,IAAU,CAAM;CAClB,GAEM,KAAW,QAAkB;EACjC,IAAM,IACH,EAAU,SAAS,QAClB,uBACF,KAA4B,EAAU;EACxC,IAAI,CAAC,GAAQ;EACb,IAAM,IAAI,EAAO,sBAAsB,GACjC,IAAQ,KAAK,IAAI,IAAW,KAAK,IAAI,IAAW,EAAE,KAAK,CAAC,GAE1D,IAAO,EAAE,OAAO,IAAQ;EAC5B,AAAI,IAAO,MACT,IAAO,KAAK,IAAI,EAAE,QAAQ,GAAK,OAAO,aAAa,IAAQ,CAAG;EAEhE,IAAM,IAAS,KAAK,IAAI,EAAE,QAAQ,OAAO,cAAc,EAAO,GACxD,IAAM,KAAK,IACf,KAAK,IAAI,GAAK,EAAE,GAAG,GACnB,OAAO,cAAc,IAAM,CAC7B;EACA,GAAQ;GAAE;GAAK;GAAM;GAAO;EAAO,CAAC;CACtC,GAAG,CAAC,CAAC;CAEL,SAAgC;EAC9B,IAAI,CAAC,KAAU,CAAC,GAAQ;EACxB,GAAS;EACT,IAAM,UAAiB,GAAS;EAGhC,OAFA,OAAO,iBAAiB,UAAU,CAAQ,GAC1C,OAAO,iBAAiB,UAAU,GAAU,EAAI,SACnC;GAEX,AADA,OAAO,oBAAoB,UAAU,CAAQ,GAC7C,OAAO,oBAAoB,UAAU,GAAU,EAAI;EACrD;CACF,GAAG;EAAC;EAAQ;EAAQ;CAAQ,CAAC;CAI7B,IAAM,KAAW,MAAS;CAE1B,QAAgB;EACd,IAAI,CAAC,KAAU,CAAC,KAAU,CAAC,MAAgB,CAAC,IAAU;EACtD,IAAM,IAAS,EAAU,SACnB,IAAS,EAAU,SAEnB,UACJ,MAAM,KACJ,GAAQ,iBACN,4EACF,KAAK,CAAC,CACR,CAAC,CAAC,QAAQ,MAAO,CAAC,EAAG,aAAa,UAAU,CAAC;EAE/C,CAAC,EAAU,CAAC,CAAC,MAAM,EAAA,EAAS,MAAM;EAElC,IAAM,KAAa,MAAyB;GAC1C,IAAI,EAAM,QAAQ,UAAU;IAE1B,AADA,EAAM,gBAAgB,GACtB,GAAQ,EAAK;IACb;GACF;GACA,IAAI,EAAM,QAAQ,OAAO;GACzB,IAAM,IAAQ,EAAU;GACxB,IAAI,EAAM,WAAW,GAAG;IAEtB,AADA,EAAM,eAAe,GACrB,GAAQ,MAAM;IACd;GACF;GACA,IAAM,IAAQ,EAAM,IACd,IAAO,EAAM,EAAM,SAAS,IAC5B,IAAS,SAAS;GACxB,AAAI,EAAM,aAAa,MAAW,KAAS,MAAW,MACpD,EAAM,eAAe,GACrB,EAAK,MAAM,KACF,CAAC,EAAM,YAAY,MAAW,MACvC,EAAM,eAAe,GACrB,EAAM,MAAM;EAEhB,GAEM,KAAiB,MAAwB;GAC7C,IAAM,IAAS,EAAM;GACjB,GAAQ,SAAS,CAAM,KAAK,GAAQ,SAAS,CAAM,KACvD,GAAQ,EAAK;EACf;EAIA,OAFA,SAAS,iBAAiB,WAAW,GAAW,EAAI,GACpD,SAAS,iBAAiB,eAAe,GAAe,EAAI,SAC/C;GAGX,AAFA,SAAS,oBAAoB,WAAW,GAAW,EAAI,GACvD,SAAS,oBAAoB,eAAe,GAAe,EAAI,GAC/D,GAAQ,MAAM;EAChB;CACF,GAAG;EAAC;EAAQ;EAAQ;EAAc;EAAU;CAAO,CAAC;CAEpD,IAAM,KAAY,KAAS,GAErB,KACJ,kBAAC,GAAD;EACE,cAAY,KAAa,EAAE;EAC3B,eAAe,GAAK,EAAS;EAC7B,SAAQ;EACR,OAAM;EACN,MAAK;EACL,WAAW,EAAO;EAClB,OAAO;EAEN,UAAS,EAAT,IAAU,IAAkC,GAAnC;GAAO,MAAM;GAAI,eAAA;EAAa,CAAiC;CAC/D,CAAA,GAIR,KAAc,CAAC,KACnB,kBAAA,GAAA,EAAA,UAAA,CACG,KACC,kBAAC,GAAD;EACE,cAAY,KAAc,EAAE;EAC5B,SAAS;EACT,SAAQ;EACR,OAAM;EACN,MAAK;EACL,WAAW,EAAO;EAClB,OAAO;EAEP,UAAA,kBAAC,GAAD;GAAO,MAAM;GAAI,eAAA;EAAa,CAAA;CACpB,CAAA,GAEb,KACC,kBAAC,GAAD;EACE,KAAK;EACL,cAAY,MAAa,EAAE;EAC3B,SAAS;EACT,SAAQ;EACR,OAAM;EACN,MAAK;EACL,WAAW,EAAO;EAClB,OAAO;EAEP,UAAA,kBAAC,GAAD;GAAQ,MAAM;GAAI,eAAA;EAAa,CAAA;CACrB,CAAA,CAEd,EAAA,CAAA,GAIE,KAAU,IACd,kBAAA,GAAA,EAAA,UAAA;EACE,kBAAC,YAAD;GACE,KAAK;GACL,OAAO;GACP,WAAW,MAAU;IAEnB,AADA,EAAS,EAAM,OAAO,KAAK,GAC3B,EAAS,IAAI;GACf;GACA,mBAAiB;GACjB,gBAAc,MAAU,QAAc,KAAA;GACtC,oBAAkB,MAAU,OAAiB,KAAA,IAAV;GACnC,YAAY;GACZ,gBAAe;GACf,cAAa;GACb,WAAW,EAAO;EACnB,CAAA;EACA,MAAU,QACT,kBAAC,KAAD;GAAG,IAAI;GAAS,MAAK;GAAQ,WAAW,EAAO;GAC5C,UAAA,GAAG,MAAoB,EAAE,YAAY,IAAI;EACzC,CAAA;EAEL,kBAAC,OAAD;GAAK,WAAW,EAAO;GAAvB,UAAA,CACE,kBAAC,GAAD;IACE,SAAS;IACT,SAAQ;IACR,OAAM;IACN,MAAK;IACL,WAAW,EAAO;IAClB,OAAO;IAEN,UAAA,MAAe,EAAE;GACZ,CAAA,GACR,kBAAC,GAAD;IAAQ,SAAS;IAAO,SAAQ;IAAQ,MAAK;IAC1C,UAAA,MAAc,EAAE;GACX,CAAA,CACL;;CACL,EAAA,CAAA,IAEF,kBAAC,OAAD;EAAK,UAAU;EAAG,WAAW,EAAO;EACjC,UAAA;CACE,CAAA;CAGP,OACE,kBAAC,OAAD;EAAK,WAAW,EAAO;EAAS,kBAAgB;EAAhD,UAAA;GACG,CAAC,MACA,kBAAC,UAAD;IACE,KAAK;IACL,MAAK;IACL,iBAAe;IACf,iBAAe;IACf,kBAAgB;IAChB,eAAe,GAAQ,CAAC,CAAM;IAC9B,WAAW,EAAO;IAPpB,UAAA,CASE,kBAAC,QAAD;KAAM,WAAW,EAAO;KAAxB,UAAA,CACE,kBAAC,GAAD;MAAQ,MAAM;MAAI,eAAA;KAAa,CAAA,GAC9B,CACG;IACN,CAAA,GAAA,kBAAC,QAAD;KAAM,WAAW,EAAO;KACtB,UAAA,kBAAC,GAAD;MAAc,MAAM;MAAI,eAAA;KAAa,CAAA;IACjC,CAAA,CACA;;GAGT,CAAC,KAAU,KACV,kBAAC,OAAD;IACE,IAAI;IACJ,WAAW,EAAO;IAClB,kBAAgB;IAHlB,UAAA,CAKE,kBAAC,OAAD;KAAK,WAAW,EAAO;KAAvB,UAAA,CACE,kBAAC,QAAD;MAAM,IAAI;MAAU,UAAA,KAAS;KAAY,CAAA,GACzC,kBAAC,QAAD;MAAM,WAAW,EAAO;MAAxB,UAAA,CACG,IACA,EACG;KACH,CAAA,CAAA;IACJ,CAAA,GAAA,EACE;;GAGN,KACC,KACA,MACA,GACE,kBAAC,OAAD;IACE,KAAK;IACL,IAAI;IACJ,MAAK;IACL,mBAAiB;IACjB,UAAU;IACV,WAAW,EAAO;IAClB,OACE,IACI;KACE,KAAK,EAAK;KACV,MAAM,EAAK;KACX,OAAO,EAAK;KACZ,QAAQ,EAAK;IACf,IACA,EAAE,YAAY,SAAS;IAf/B,UAAA;KAkBE,kBAAC,OAAD;MAAK,WAAW,EAAO;MAAvB,UAAA,CACE,kBAAC,QAAD;OAAM,WAAW,EAAO;OAAxB,UAAA,CACE,kBAAC,QAAD;QAAM,IAAI;QAAU,UAAA,KAAS;OAAY,CAAA,GACxC,KAAS,QACR,kBAAC,QAAD;QAAM,WAAW,EAAO;QAAO,gBAAa;QACzC,UAAA;OACG,CAAA,CAEJ;MACN,CAAA,GAAA,kBAAC,QAAD;OAAM,WAAW,EAAO;OAAxB,UAAA,CACG,IACD,kBAAC,GAAD;QACE,cAAY,MAAc,EAAE;QAC5B,eAAe,GAAQ,EAAK;QAC5B,SAAQ;QACR,OAAM;QACN,MAAK;QACL,WAAW,EAAO;QAClB,OAAO;QAEP,UAAA,kBAAC,IAAD;SAAG,MAAM;SAAI,eAAA;QAAa,CAAA;OAChB,CAAA,CACR;MACH,CAAA,CAAA;;KACJ;KACA,CAAC,KACA,kBAAC,OAAD;MAAK,WAAW,EAAO;MACrB,UAAA,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,GAAK,CAAI;OACxB,WAAW,EAAO;OAHpB,UAAA,CAMI,EADD,IACE,IAEA,GAFD;QAAO,MAAM;QAAI,eAAA;OAAa,CAED,GAE9B,IAAS,EAAE,SAAU,KAAiB,EAAE,QACnC;;KACL,CAAA;IAEJ;GACL,CAAA,GAAA,EACF;GAEF,kBAAC,QAAD;IAAM,MAAK;IAAS,aAAU;IAAS,WAAW,EAAO;IACtD,UAAA,IAAS,EAAE,SAAS;GACjB,CAAA;EACH;;AAET;;;ACxeA,SAAgB,GAAe,EAC7B,aACA,iBAAc,UACd,cACA,QACA,kBAAe,YACf,cACA,kBAAe,WACf,eACsB;CACtB,IAAM,EAAE,YAAS,EAAQ;CAOzB,OACE,kBAAC,OAAD;EAAK,WAAW,EAAO;EAAvB,UAAA;GACG,KACC,kBAAC,GAAD;IACE,SAAQ;IACR,OAAM;IACN,MAAK;IACL,WAAW,kBAAC,GAAD,EAAW,eAAA,GAAa,CAAA;IACnC,SAAS;IAER,UAAA;GACK,CAAA;GAET,KACC,kBAAC,GAAD;IACE,SAAQ;IACR,OAAM;IACN,MAAK;IACL,WAAW,kBAAC,GAAD,EAAQ,eAAA,GAAa,CAAA;IAChC,SAAS,YAxBiB;KAEhC,AADI,KAAO,QAAM,MAAM,EAAK,CAAG,GAC/B,IAAY;IACd;IAuBS,UAAA;GACK,CAAA;GAET,KACC,kBAAC,GAAD;IACE,SAAQ;IACR,MAAK;IACL,SAAS,kBAAC,GAAD,EAAc,eAAA,GAAa,CAAA;IACpC,SAAS;IACT,OAAO,EAAE,mBAAmB,OAAO;IAElC,UAAA;GACK,CAAA;GAET;EACE;;AAET"}
|