@salesql/sql_components_vue3 1.0.0 → 2.1.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/dist/constants/events.d.ts +20 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +40 -6
- package/dist/index.es.js +2258 -613
- package/dist/index.es.js.map +1 -1
- package/dist/sql_components_vue3.css +1 -1
- package/dist/ui/DsAlert/DsAlert.vue.d.ts +15 -2
- package/dist/ui/DsAvatar/DsAvatar.types.d.ts +6 -0
- package/dist/ui/DsAvatar/DsAvatar.vue.d.ts +47 -2
- package/dist/ui/DsButton/DsButton.types.d.ts +6 -1
- package/dist/ui/DsButton/DsButton.vue.d.ts +11 -5
- package/dist/ui/DsCell/DsCell.types.d.ts +19 -0
- package/dist/ui/DsCell/DsCell.vue.d.ts +71 -0
- package/dist/ui/DsCell/index.d.ts +2 -0
- package/dist/ui/DsCheckbox/DsCheckbox.types.d.ts +2 -0
- package/dist/ui/DsCheckbox/DsCheckbox.vue.d.ts +10 -1
- package/dist/ui/DsContactValue/DsContactValue.types.d.ts +4 -0
- package/dist/ui/DsContactValue/DsContactValue.vue.d.ts +18 -0
- package/dist/ui/DsCustomTag/DsCustomTag.types.d.ts +9 -0
- package/dist/ui/DsCustomTag/DsCustomTag.vue.d.ts +29 -0
- package/dist/ui/DsCustomTag/index.d.ts +2 -0
- package/dist/ui/DsDataCard/DsDataCard.types.d.ts +2 -0
- package/dist/ui/DsDataCard/DsDataCard.vue.d.ts +58 -1
- package/dist/ui/DsEmptyState/DsEmptyState.types.d.ts +12 -0
- package/dist/ui/DsEmptyState/DsEmptyState.vue.d.ts +53 -0
- package/dist/ui/DsEmptyState/index.d.ts +2 -0
- package/dist/ui/DsInput/DsInput.vue.d.ts +36 -2
- package/dist/ui/DsItemSelectorButton/DsItemSelectorButton.types.d.ts +20 -0
- package/dist/ui/DsItemSelectorButton/DsItemSelectorButton.vue.d.ts +50 -0
- package/dist/ui/DsItemSelectorButton/index.d.ts +2 -0
- package/dist/ui/DsMenuItem/DsMenuItem.types.d.ts +6 -0
- package/dist/ui/DsMenuItem/DsMenuItem.vue.d.ts +42 -0
- package/dist/ui/DsMenuItem/index.d.ts +2 -0
- package/dist/ui/DsModal/DsModal.types.d.ts +10 -0
- package/dist/ui/DsModal/DsModal.vue.d.ts +42 -3
- package/dist/ui/DsNotification/DsNotification.types.d.ts +8 -0
- package/dist/ui/DsNotification/DsNotification.vue.d.ts +22 -2
- package/dist/ui/DsPaginator/DsPaginator.types.d.ts +12 -0
- package/dist/ui/DsPaginator/DsPaginator.vue.d.ts +101 -0
- package/dist/ui/DsPaginator/index.d.ts +2 -0
- package/dist/ui/DsPill/DsPill.types.d.ts +11 -0
- package/dist/ui/DsPill/DsPill.vue.d.ts +46 -0
- package/dist/ui/DsPill/index.d.ts +2 -0
- package/dist/ui/DsPillWithBorder/DsPillWithBorder.types.d.ts +2 -0
- package/dist/ui/DsPillWithBorder/DsPillWithBorder.vue.d.ts +18 -0
- package/dist/ui/DsPillWithBorder/index.d.ts +2 -0
- package/dist/ui/DsRadio/DsRadio.types.d.ts +7 -0
- package/dist/ui/DsRadio/DsRadio.vue.d.ts +55 -0
- package/dist/ui/DsRadio/index.d.ts +2 -0
- package/dist/ui/DsRow/DsRow.types.d.ts +6 -0
- package/dist/ui/DsRow/DsRow.vue.d.ts +37 -0
- package/dist/ui/DsRow/index.d.ts +2 -0
- package/dist/ui/DsSegmentedProgressBar/DsSegmentedProgressBar.types.d.ts +5 -0
- package/dist/ui/DsSegmentedProgressBar/DsSegmentedProgressBar.vue.d.ts +62 -0
- package/dist/ui/DsSegmentedProgressBar/index.d.ts +2 -0
- package/dist/ui/DsSelect/DsSelect.types.d.ts +33 -0
- package/dist/ui/DsSelect/DsSelect.vue.d.ts +126 -0
- package/dist/ui/DsSelect/index.d.ts +2 -0
- package/dist/ui/DsSpinner/DsSpinner.types.d.ts +6 -0
- package/dist/ui/DsSpinner/DsSpinner.vue.d.ts +4 -4
- package/dist/ui/DsSwitch/DsSwitch.types.d.ts +6 -0
- package/dist/ui/DsSwitch/DsSwitch.vue.d.ts +30 -0
- package/dist/ui/DsSwitch/index.d.ts +2 -0
- package/dist/ui/DsTable/DsTable.types.d.ts +11 -0
- package/dist/ui/DsTable/DsTable.vue.d.ts +112 -10
- package/dist/ui/DsTag/DsTag.types.d.ts +1 -0
- package/dist/ui/DsTag/DsTag.vue.d.ts +6 -0
- package/dist/ui/DsTextarea/DsTextarea.types.d.ts +8 -0
- package/dist/ui/DsTextarea/DsTextarea.vue.d.ts +14 -3
- package/dist/ui/DsTooltip/DsTooltip.types.d.ts +7 -0
- package/dist/ui/DsTooltip/DsTooltip.vue.d.ts +64 -0
- package/dist/ui/DsTooltip/index.d.ts +2 -0
- package/dist/ui/DsWizardStepper/DsWizardStepper.types.d.ts +13 -0
- package/dist/ui/DsWizardStepper/DsWizardStepper.vue.d.ts +58 -0
- package/dist/ui/DsWizardStepper/index.d.ts +2 -0
- package/package.json +1 -1
package/dist/index.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.es.js","sources":["../src/ui/DsSpinner/DsSpinner.types.ts","../src/ui/DsSpinner/DsSpinner.vue","../src/constants/events.ts","../src/ui/DsButton/DsButton.types.ts","../src/ui/DsButton/DsButton.vue","../src/ui/DsTag/DsTag.types.ts","../src/ui/DsTag/DsTag.vue","../src/ui/DsBadge/DsBadge.types.ts","../src/ui/DsBadge/DsBadge.vue","../src/ui/DsAvatar/DsAvatar.vue","../src/ui/DsTable/DsTable.types.ts","../src/ui/DsTable/DsTable.vue","../src/ui/DsInput/DsInput.types.ts","../src/ui/DsInput/DsInput.vue","../src/ui/DsTextarea/DsTextarea.vue","../src/ui/DsCheckbox/DsCheckbox.vue","../src/ui/DsAlert/DsAlert.types.ts","../src/ui/DsAlert/DsAlert.vue","../src/ui/DsNotification/DsNotification.types.ts","../src/ui/DsNotification/DsNotification.vue","../src/ui/DsModal/DsModal.vue","../src/ui/DsVerificationIcon/DsVerificationIcon.types.ts","../src/ui/DsVerificationIcon/DsVerificationIcon.vue","../src/ui/DsContactValue/DsContactValue.vue","../src/ui/DsDataCard/DsDataCard.vue"],"sourcesContent":["// DsSpinner — the DS \"Spinner\": a rotating border-ring. Tones mirror Spinner.jsx.\n\nexport const DS_SPINNER_TONES = {\n DARK: 'dark', // slate ring — for light surfaces (default)\n LIGHT: 'light', // white ring — for dark surfaces / solid buttons\n ACCENT: 'accent', // accent ring\n} as const;\nexport type DsSpinnerTone = (typeof DS_SPINNER_TONES)[keyof typeof DS_SPINNER_TONES];\n","<template>\n <span\n class=\"ds-spinner\"\n :class=\"`ds-spinner--${tone}`\"\n role=\"status\"\n :style=\"{ width: `${size}px`, height: `${size}px` }\"\n :aria-label=\"label || undefined\"\n :aria-hidden=\"label ? undefined : 'true'\"\n >\n <span v-if=\"label\" class=\"ds-spinner__sr-only\">{{ label }}</span>\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport { type PropType } from 'vue';\nimport { DS_SPINNER_TONES, type DsSpinnerTone } from './DsSpinner.types';\n\ndefineProps({\n size: { type: Number, default: 24 },\n tone: {\n type: String as PropType<DsSpinnerTone>,\n default: DS_SPINNER_TONES.DARK,\n },\n // Optional accessible text; without it the spinner is aria-hidden.\n label: { type: String, default: '' },\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/motion.scss' as m;\n\n@keyframes ds-spinner-rot {\n to {\n transform: rotate(360deg);\n }\n}\n\n.ds-spinner {\n display: inline-block;\n\n border: 2px solid;\n border-radius: 50%;\n\n animation: ds-spinner-rot 1s linear infinite;\n\n @include m.reduce-motion {\n animation: none;\n }\n\n &--dark {\n border-color: colors.$slate-tint;\n border-top-color: colors.$slate-main;\n }\n &--light {\n border-color: rgba(255, 255, 255, 0.4);\n border-top-color: colors.$white;\n }\n &--accent {\n border-color: colors.$accent-mid;\n border-top-color: colors.$accent-main;\n }\n\n &__sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n}\n</style>\n","// Evento genérico de click para props/emit\nexport const CLICK = 'click' as const;\nexport type ClickEventName = typeof CLICK;\n\n// Evento de cierre (p. ej. DsTag con × / closable) — mapea a onClose del DS\nexport const CLOSE = 'close' as const;\nexport type CloseEventName = typeof CLOSE;\n\n// Eventos de foco de controles de formulario (DsInput / DsTextarea)\nexport const FOCUS = 'focus' as const;\nexport type FocusEventName = typeof FOCUS;\n\nexport const BLUR = 'blur' as const;\nexport type BlurEventName = typeof BLUR;\n\n// Eventos de la familia data (DsContactValue / DsDataCard).\n// COPY lleva el valor copiado; CORRECT / INCORRECT marcan el rating de un dato.\nexport const COPY = 'copy' as const;\nexport type CopyEventName = typeof COPY;\n\nexport const CORRECT = 'correct' as const;\nexport type CorrectEventName = typeof CORRECT;\n\nexport const INCORRECT = 'incorrect' as const;\nexport type IncorrectEventName = typeof INCORRECT;\n\n// Acciones de cabecera de DsDataCard.\nexport const SORT = 'sort' as const;\nexport type SortEventName = typeof SORT;\n\nexport const EDIT = 'edit' as const;\nexport type EditEventName = typeof EDIT;\n","// DsButton — 1:1 with the SalesQL Design System \"Button\" specimen.\n// Variants/sizes are the ONLY ones the DS defines; more arrive when the DS grows.\n\n// Variants (classes: ds-button--<variant>)\nexport const DS_BUTTON_VARIANTS = {\n PRIMARY: 'primary', // solid accent fill, white text\n SECONDARY: 'secondary', // slate surface fill, dark text\n TERTIARY: 'tertiary', // transparent ghost, dark text\n QUATERNARY: 'quaternary', // accent border + accent text, light fill\n SPECIAL: 'special', // accent text only, no fill/border (link-style)\n} as const;\nexport type DsButtonVariant = (typeof DS_BUTTON_VARIANTS)[keyof typeof DS_BUTTON_VARIANTS];\n\n// Sizes = DS control heights (classes: ds-button--<size>)\nexport const DS_BUTTON_SIZES = {\n SMALL: 'small', // 24px\n MEDIUM: 'medium', // 32px\n LARGE: 'large', // 36px\n XLARGE: 'xlarge', // 48px\n} as const;\nexport type DsButtonSize = (typeof DS_BUTTON_SIZES)[keyof typeof DS_BUTTON_SIZES];\n\n// Native <button> type\nexport const DS_BUTTON_NATIVE_TYPES = {\n BUTTON: 'button',\n SUBMIT: 'submit',\n RESET: 'reset',\n} as const;\nexport type DsButtonNativeType =\n (typeof DS_BUTTON_NATIVE_TYPES)[keyof typeof DS_BUTTON_NATIVE_TYPES];\n","<template>\n <button\n class=\"ds-button\"\n :type=\"nativeType\"\n :disabled=\"disabled || loading\"\n :autofocus=\"autofocus\"\n :aria-busy=\"loading || undefined\"\n :aria-disabled=\"disabled || loading || undefined\"\n :aria-label=\"ariaLabel || undefined\"\n :class=\"[\n `ds-button--${variant}`,\n `ds-button--${size}`,\n {\n 'is-disabled': disabled || loading,\n 'is-loading': loading,\n 'is-circle': circle,\n 'is-dashed': dashed,\n },\n ]\"\n @click=\"handleClick\"\n >\n <DsSpinner v-if=\"loading\" class=\"ds-button__spinner\" :size=\"iconSize\" :tone=\"spinnerTone\" />\n\n <span\n v-if=\"!loading && (leftIcon || $slots.leading)\"\n class=\"ds-button__icon ds-button__icon--leading\"\n >\n <slot name=\"leading\"\n ><i class=\"material-icons\" aria-hidden=\"true\">{{ leftIcon }}</i></slot\n >\n </span>\n\n <span v-if=\"!loading && $slots.default\" class=\"ds-button__label\"><slot /></span>\n\n <span\n v-if=\"!loading && (rightIcon || $slots.trailing)\"\n class=\"ds-button__icon ds-button__icon--trailing\"\n >\n <slot name=\"trailing\"\n ><i class=\"material-icons\" aria-hidden=\"true\">{{ rightIcon }}</i></slot\n >\n </span>\n </button>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, type PropType } from 'vue';\nimport { DsSpinner, DS_SPINNER_TONES } from '@shared/ui/DsSpinner';\nimport { CLICK } from '@shared/constants/events';\nimport {\n DS_BUTTON_NATIVE_TYPES,\n DS_BUTTON_SIZES,\n DS_BUTTON_VARIANTS,\n type DsButtonNativeType,\n type DsButtonSize,\n type DsButtonVariant,\n} from './DsButton.types';\n\nconst props = defineProps({\n variant: { type: String as PropType<DsButtonVariant>, default: DS_BUTTON_VARIANTS.PRIMARY },\n size: { type: String as PropType<DsButtonSize>, default: DS_BUTTON_SIZES.MEDIUM },\n nativeType: {\n type: String as PropType<DsButtonNativeType>,\n default: DS_BUTTON_NATIVE_TYPES.BUTTON,\n },\n // Material Icon names (rendered via <i class=\"material-icons\">); a leading/\n // trailing slot overrides. Icon-only buttons need ariaLabel.\n leftIcon: { type: String, default: '' },\n rightIcon: { type: String, default: '' },\n ariaLabel: { type: String, default: '' },\n disabled: Boolean,\n loading: Boolean,\n circle: Boolean,\n dashed: Boolean,\n autofocus: Boolean,\n});\n\n// Runtime array (not the type-only form): the SFC compiler can't resolve\n// `typeof CLICK` to a literal, leaving `emits` empty at runtime. With `click`\n// undeclared, the parent's @click both falls through to the native <button>\n// AND is invoked by emit() → the listener fires twice. Declaring it at runtime\n// (via the CLICK constant, per convention) excludes it from fallthrough.\nconst emit = defineEmits([CLICK]);\n\nconst iconSize = computed(() => (props.size === DS_BUTTON_SIZES.SMALL ? 14 : 20));\nconst spinnerTone = computed(() =>\n props.variant === DS_BUTTON_VARIANTS.PRIMARY ? DS_SPINNER_TONES.LIGHT : DS_SPINNER_TONES.DARK\n);\n\nfunction handleClick(event: MouseEvent) {\n if (props.disabled || props.loading) return;\n emit(CLICK, event);\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/spacing.scss' as s;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n@use '../../assets/css/icons.scss' as i;\n\n.ds-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n\n padding: 0;\n\n font-family: t.$font-family;\n font-weight: t.$weight-medium; // 500 (DS); small overrides to 400\n font-size: t.font-size('body');\n line-height: 20px;\n\n border: 1px solid transparent;\n border-radius: r.$radius-control;\n outline: none;\n\n cursor: pointer;\n transition:\n background-color m.$duration-default m.$easing,\n border-color m.$duration-default m.$easing,\n opacity m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n &.is-disabled {\n opacity: 0.55;\n cursor: not-allowed;\n }\n\n &__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n // DS icon size: 20px (small → 14px). Sizing the glyph to fill its box also\n // removes the optical \"gap\" a smaller glyph leaves against the button edge.\n .material-icons {\n font-size: i.icon-size(20);\n line-height: 1;\n }\n }\n\n // Material glyphs carry a built-in keyline (~2–3px advance bearing). Absorb it\n // on the edge side so a leading/trailing icon sits at the same optical inset as\n // a text-only button. Not for icon-only (circle) buttons — those stay centered.\n &:not(.is-circle) &__icon--leading {\n margin-left: -3px;\n }\n &:not(.is-circle) &__icon--trailing {\n margin-right: -3px;\n }\n\n &__spinner {\n /* size comes from the :size prop (iconSize) */\n }\n\n /* ---------- Variants ---------- */\n &--primary {\n color: colors.$white;\n background-color: colors.$accent-main;\n\n &:hover:not(.is-disabled) {\n background-color: colors.$accent-shadow;\n }\n }\n &--secondary {\n color: colors.$slate-main;\n background-color: colors.$slate-bg;\n\n &:hover:not(.is-disabled) {\n background-color: colors.$slate-tint;\n }\n }\n &--tertiary {\n color: colors.$slate-main;\n background-color: colors.$white;\n\n &:hover:not(.is-disabled) {\n background-color: colors.$slate-tint;\n }\n }\n &--quaternary {\n color: colors.$accent-main;\n border-color: colors.$accent-mid;\n background-color: colors.$white;\n\n &:hover:not(.is-disabled) {\n border-color: colors.$accent-main;\n }\n }\n &--special {\n color: colors.$accent-main;\n background-color: colors.$white;\n\n &:hover:not(.is-disabled) {\n background-color: colors.$accent-bg;\n }\n }\n\n /* ---------- Dashed \"add\" affordance ---------- */\n &.is-dashed {\n color: colors.$slate-mid;\n font-weight: t.$weight-bold;\n\n border: 1px dashed colors.$slate-mid;\n background-color: transparent;\n\n &:hover:not(.is-disabled) {\n color: colors.$accent-main;\n border-color: colors.$accent-main;\n background-color: colors.$accent-bg;\n }\n }\n\n /* ---------- Sizes (DS control heights + paddings) ---------- */\n &--small {\n height: s.control-height('small');\n padding: 0 s.control-padding('small');\n gap: 2px;\n\n font-weight: t.$weight-regular; // 400\n font-size: t.font-size('caption');\n\n .ds-button__icon .material-icons {\n font-size: i.icon-size(14);\n }\n }\n &--medium {\n height: s.control-height('medium');\n padding: 0 s.control-padding('medium');\n }\n &--large {\n height: s.control-height('large');\n padding: 0 s.control-padding('large');\n }\n &--xlarge {\n height: s.control-height('xlarge');\n padding: 0 s.control-padding('xlarge');\n }\n\n /* ---------- Circle (icon-only) ---------- */\n &.is-circle {\n padding: 8px;\n border-radius: r.$radius-full;\n aspect-ratio: 1;\n }\n}\n</style>\n","// DsTag — the DS \"Tag\": small bordered chip with optional close (×).\n// ⚠️ NOT the solid pill (that is DsBadge). See DESIGN-SYSTEM.md reconciliation.\n// Tones mirror the DS Tag source (Tag.jsx TONES). `rounded` → semicircular sides.\n\nexport const DS_TAG_TONES = {\n DEFAULT: 'default', // white bg, slate-tint border, muted text\n SUCCESS: 'success', // e.g. Verified\n INFO: 'info', // e.g. Work\n WARNING: 'warning', // e.g. Pending\n ERROR: 'error', // e.g. Bounced\n} as const;\nexport type DsTagTone = (typeof DS_TAG_TONES)[keyof typeof DS_TAG_TONES];\n","<template>\n <span class=\"ds-tag\" :class=\"[`ds-tag--${tone}`, { 'is-rounded': rounded }]\">\n <slot />\n\n <button\n v-if=\"closable\"\n type=\"button\"\n class=\"ds-tag__close\"\n :aria-label=\"closeLabel || undefined\"\n @click=\"emit(CLOSE)\"\n >\n <i class=\"material-icons\" aria-hidden=\"true\">close</i>\n </button>\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport { type PropType } from 'vue';\nimport { CLOSE } from '@shared/constants/events';\nimport { DS_TAG_TONES, type DsTagTone } from './DsTag.types';\n\ndefineProps({\n tone: {\n type: String as PropType<DsTagTone>,\n default: DS_TAG_TONES.DEFAULT,\n },\n // Semicircular sides (DS `rounded`): radius-round vs the default radius-control.\n rounded: Boolean,\n // Show a × close control that emits `close`.\n closable: Boolean,\n // Accessible name for the × control (the app owns the wording / i18n).\n closeLabel: {\n type: String,\n default: '',\n },\n});\n\nconst emit = defineEmits<{\n (e: typeof CLOSE): void;\n}>();\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/icons.scss' as i;\n\n.ds-tag {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n\n padding: 0 10px;\n\n color: colors.$slate-mid;\n font-family: t.$font-family;\n font-weight: t.$weight-regular;\n font-size: t.font-size('caption');\n line-height: 30px;\n\n background-color: colors.$white;\n\n border: 1px solid colors.$slate-tint;\n border-radius: r.$radius-control;\n\n white-space: nowrap;\n\n &.is-rounded {\n border-radius: r.$radius-round;\n }\n\n &__close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n padding: 0;\n\n color: inherit;\n\n background: transparent;\n border: 0;\n\n cursor: pointer;\n\n .material-icons {\n font-size: i.icon-size(12);\n }\n }\n\n /* ---------- Tones (default: white + tint border + muted text) ---------- */\n &--default {\n color: colors.$slate-mid;\n border-color: colors.$slate-tint;\n background-color: colors.$white;\n }\n &--success {\n color: colors.$success-shadow;\n border-color: colors.$success-mid;\n background-color: colors.$success-tint;\n }\n &--info {\n color: colors.$info-shadow;\n border-color: colors.$info-mid;\n background-color: colors.$info-tint;\n }\n &--warning {\n color: colors.$warning-shadow;\n border-color: colors.$warning-mid;\n background-color: colors.$warning-tint;\n }\n &--error {\n color: colors.$error-shadow;\n border-color: colors.$error-mid;\n background-color: colors.$error-tint;\n }\n}\n</style>\n","// DsBadge — the DS \"Badge\": solid, white-text pill (value types & counts).\n// ⚠️ NOT the tint chip (that is DsTag). See DESIGN-SYSTEM.md reconciliation.\n// Tones mirror the DS Badge source (Badge.jsx TONES).\n\nexport const DS_BADGE_TONES = {\n ACCENT: 'accent', // e.g. Work (purple)\n GREY: 'grey', // e.g. Direct (slate-soft)\n INFO: 'info', // e.g. Mobile (blue)\n SUCCESS: 'success', // e.g. New (green)\n ERROR: 'error', // coral\n WARNING: 'warning', // orange\n} as const;\nexport type DsBadgeTone = (typeof DS_BADGE_TONES)[keyof typeof DS_BADGE_TONES];\n","<template>\n <span class=\"ds-badge\" :class=\"`ds-badge--${tone}`\">\n <slot />\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport { type PropType } from 'vue';\nimport { DS_BADGE_TONES, type DsBadgeTone } from './DsBadge.types';\n\ndefineProps({\n tone: {\n type: String as PropType<DsBadgeTone>,\n default: DS_BADGE_TONES.ACCENT,\n },\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n\n.ds-badge {\n display: inline-flex;\n align-items: center;\n\n height: 17px;\n padding: 0 8px;\n\n color: colors.$white;\n font-family: t.$font-family;\n font-weight: t.$weight-bold;\n font-size: t.font-size('caption');\n line-height: 17px;\n\n border-radius: r.$radius-pill;\n\n white-space: nowrap;\n\n /* ---------- Tones (solid fill, white text) ---------- */\n &--accent {\n background-color: colors.$accent-main;\n }\n &--grey {\n background-color: colors.$slate-soft;\n }\n &--info {\n background-color: colors.$info-main;\n }\n &--success {\n background-color: colors.$success-main;\n }\n &--error {\n background-color: colors.$error-main;\n }\n &--warning {\n background-color: colors.$warning-main;\n }\n}\n</style>\n","<template>\n <img\n v-if=\"image\"\n class=\"ds-avatar\"\n :class=\"{ 'is-circular': circular }\"\n :src=\"image\"\n :alt=\"alt\"\n :style=\"boxStyle\"\n />\n\n <div\n v-else-if=\"initials\"\n class=\"ds-avatar ds-avatar--initials\"\n :class=\"{ 'is-circular': circular }\"\n role=\"img\"\n :aria-label=\"ariaLabel || undefined\"\n :style=\"[\n boxStyle,\n { background: hashed.bg, color: hashed.fg, fontSize: `${Math.round(size * 0.4)}px` },\n ]\"\n >\n {{ initials }}\n </div>\n\n <div\n v-else\n class=\"ds-avatar ds-avatar--placeholder\"\n :class=\"{ 'is-circular': circular }\"\n :style=\"boxStyle\"\n aria-hidden=\"true\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue';\n\nconst props = defineProps({\n image: { type: String, default: '' },\n firstName: { type: String, default: '' },\n lastName: { type: String, default: '' },\n uid: { type: String, default: '' },\n size: { type: Number, default: 40 },\n circular: Boolean,\n // Accessible label (defaults to \"firstName lastName\").\n alt: { type: String, default: '' },\n});\n\nconst initials = computed(() =>\n props.firstName && props.lastName\n ? (props.firstName.charAt(0) + props.lastName.charAt(0)).toUpperCase()\n : ''\n);\n\nconst ariaLabel = computed(() => props.alt || `${props.firstName} ${props.lastName}`.trim());\n\nconst boxStyle = computed(() => ({ width: `${props.size}px`, height: `${props.size}px` }));\n\n// Deterministic color from a uid — exact port of SqlAvatar.getBackgroundColor.\nconst hashed = computed(() => {\n const seed = props.uid || props.firstName + props.lastName;\n let hash = 0;\n for (let i = 0; i < seed.length; i++) {\n hash = seed.charCodeAt(i) + ((hash << 5) - hash);\n hash = hash & hash;\n }\n const rgb = [0, 0, 0];\n for (let i = 0; i < 3; i++) rgb[i] = (hash >> (i * 8)) & 255;\n const light = rgb[0] >= 200 && rgb[1] >= 200 && rgb[2] >= 200;\n return { bg: `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`, fg: light ? '#323f4b' : '#fff' };\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n\n.ds-avatar {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n\n overflow: hidden;\n object-fit: cover;\n\n border-radius: r.$radius-control;\n\n &.is-circular {\n border-radius: r.$radius-full;\n }\n\n &--initials {\n font-family: t.$font-family;\n font-weight: t.$weight-black;\n line-height: 1;\n user-select: none;\n }\n\n &--placeholder {\n background-color: #c0c4cc; // DS placeholder grey (Avatar.jsx)\n }\n}\n</style>\n","// DsTable — the DS data table (data-dense density). Column/row API with hairline\n// borders, row hover, and an optional highlighted \"most likely\" row.\n\nexport const DS_TABLE_ALIGN = {\n LEFT: 'left',\n CENTER: 'center',\n RIGHT: 'right',\n} as const;\nexport type DsTableAlign = (typeof DS_TABLE_ALIGN)[keyof typeof DS_TABLE_ALIGN];\n\n// A column. `key` maps to the row property for default content and names the\n// scoped slot `cell-<key>`.\nexport interface DsTableColumn {\n key: string;\n label: string;\n align?: DsTableAlign;\n width?: string;\n}\n\n// A row: only a stable `id` is required (for :key and highlighting); the rest of\n// the properties are consumed per column via its `key`.\nexport interface DsTableRow {\n id: string | number;\n [key: string]: unknown;\n}\n","<script setup lang=\"ts\">\nimport { type PropType } from 'vue';\nimport { DS_TABLE_ALIGN, type DsTableColumn, type DsTableRow } from './DsTable.types';\n\ndefineProps({\n columns: {\n type: Array as PropType<DsTableColumn[]>,\n required: true,\n },\n rows: {\n type: Array as PropType<DsTableRow[]>,\n required: true,\n },\n emptyMessage: {\n type: String,\n default: '',\n },\n // id of the row to highlight (the DS \"most likely\" row → accent surface).\n highlightRowId: {\n type: [String, Number] as PropType<string | number | null>,\n default: null,\n },\n});\n</script>\n\n<template>\n <div class=\"ds-table\">\n <div class=\"ds-table__wrapper\">\n <table class=\"ds-table__table\">\n <thead>\n <tr>\n <th\n v-for=\"column in columns\"\n :key=\"column.key\"\n :class=\"`ds-table__th--${column.align ?? DS_TABLE_ALIGN.LEFT}`\"\n :style=\"column.width ? { width: column.width } : undefined\"\n >\n {{ column.label }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr v-if=\"rows.length === 0\">\n <td class=\"ds-table__empty\" :colspan=\"columns.length\" data-testid=\"ds-table-empty\">\n <slot name=\"empty\">{{ emptyMessage }}</slot>\n </td>\n </tr>\n <tr\n v-for=\"row in rows\"\n :key=\"row.id\"\n :class=\"{ 'is-highlighted': row.id === highlightRowId }\"\n data-testid=\"ds-table-row\"\n >\n <td\n v-for=\"column in columns\"\n :key=\"column.key\"\n :class=\"`ds-table__td--${column.align ?? DS_TABLE_ALIGN.LEFT}`\"\n >\n <slot :name=\"`cell-${column.key}`\" :row=\"row\">{{ row[column.key] }}</slot>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/spacing.scss' as s;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n\n.ds-table {\n &__wrapper {\n overflow-x: auto;\n\n border: 1px solid colors.$slate-tint;\n border-radius: r.$radius-card-lg;\n }\n\n &__table {\n width: 100%;\n border-collapse: collapse;\n\n // Own every border/height explicitly so host-app global table rules can't\n // leak in; scoped specificity (block + [data-v]) beats them.\n th {\n padding: s.spacing(1) s.spacing(2);\n\n color: colors.$slate-main;\n font-weight: t.$weight-bold;\n font-size: t.font-size('body');\n\n border: 0;\n border-bottom: 1px solid colors.$slate-tint;\n\n background-color: colors.$slate-bg;\n }\n\n td {\n height: auto;\n padding: s.spacing(1) s.spacing(2);\n\n color: colors.$slate-main;\n font-size: t.font-size('body');\n\n border: 0;\n }\n\n tr {\n border: 0;\n transition: background-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n }\n\n // Row separator only BETWEEN adjacent body rows.\n tbody tr + tr {\n border-top: 1px solid colors.$slate-tint;\n }\n\n // Row hover (data-dense affordance).\n tbody tr:hover {\n background-color: colors.$slate-bg;\n }\n\n // The highlighted \"most likely\" row wins over hover.\n tbody tr.is-highlighted {\n background-color: colors.$accent-bg;\n }\n tbody tr.is-highlighted:hover {\n background-color: colors.$accent-tint;\n }\n }\n\n &__th--left,\n &__td--left {\n text-align: left;\n }\n &__th--center,\n &__td--center {\n text-align: center;\n }\n &__th--right,\n &__td--right {\n text-align: right;\n }\n\n &__empty {\n padding: s.spacing(5) s.spacing(3);\n\n color: colors.$slate-soft;\n text-align: center;\n }\n}\n</style>\n","// DsInput — the DS \"Input\": single-line field. Prop vocabulary mirrors Input.jsx.\n// Border: primary-main at rest → accent on focus, coral on error. NO bold-on-focus\n// (the DS source keeps the text at weight 400). Sizes are the DS input heights.\n\nexport const DS_INPUT_TYPES = {\n TEXT: 'text',\n EMAIL: 'email',\n PASSWORD: 'password',\n TEL: 'tel',\n NUMBER: 'number',\n SEARCH: 'search',\n URL: 'url',\n} as const;\nexport type DsInputType = (typeof DS_INPUT_TYPES)[keyof typeof DS_INPUT_TYPES];\n\nexport const DS_INPUT_SIZES = {\n SM: 'sm', // 36px\n MD: 'md', // 48px\n} as const;\nexport type DsInputSize = (typeof DS_INPUT_SIZES)[keyof typeof DS_INPUT_SIZES];\n","<template>\n <div\n class=\"ds-input\"\n :class=\"[`ds-input--${size}`, { 'is-error': error, 'is-disabled': disabled }]\"\n >\n <div class=\"ds-input__wrap\">\n <input\n :id=\"id || undefined\"\n v-model=\"model\"\n class=\"ds-input__control\"\n :class=\"{ 'has-suffix': hasSuffix }\"\n :type=\"resolvedType\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :name=\"name || undefined\"\n :autocomplete=\"autocomplete || undefined\"\n :aria-invalid=\"error || undefined\"\n :aria-describedby=\"error && errorMessage && id ? `${id}-error` : undefined\"\n @focus=\"emit(FOCUS, $event)\"\n @blur=\"emit(BLUR, $event)\"\n />\n\n <div v-if=\"hasSuffix\" class=\"ds-input__suffix\">\n <slot name=\"suffix\" />\n\n <i\n v-if=\"error && showErrorIcon\"\n class=\"material-icons ds-input__error-icon\"\n aria-hidden=\"true\"\n >warning</i\n >\n\n <button\n v-else-if=\"isPassword\"\n type=\"button\"\n class=\"ds-input__toggle\"\n :aria-label=\"toggleLabel || undefined\"\n :aria-pressed=\"reveal\"\n @click=\"reveal = !reveal\"\n >\n <i class=\"material-icons\" aria-hidden=\"true\">{{\n reveal ? 'visibility' : 'visibility_off'\n }}</i>\n </button>\n </div>\n </div>\n\n <p v-if=\"error && errorMessage\" :id=\"id ? `${id}-error` : undefined\" class=\"ds-input__error\">\n {{ errorMessage }}\n </p>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, useSlots, type PropType } from 'vue';\nimport { FOCUS, BLUR } from '@shared/constants/events';\nimport {\n DS_INPUT_TYPES,\n DS_INPUT_SIZES,\n type DsInputType,\n type DsInputSize,\n} from './DsInput.types';\n\nconst model = defineModel<string>({ default: '' });\n\nconst props = defineProps({\n type: { type: String as PropType<DsInputType>, default: DS_INPUT_TYPES.TEXT },\n size: { type: String as PropType<DsInputSize>, default: DS_INPUT_SIZES.SM },\n placeholder: { type: String, default: '' },\n disabled: Boolean,\n error: Boolean,\n errorMessage: { type: String, default: '' },\n showErrorIcon: Boolean,\n name: { type: String, default: '' },\n id: { type: String, default: '' },\n autocomplete: { type: String, default: '' },\n // Accessible name for the password visibility toggle.\n toggleLabel: { type: String, default: '' },\n});\n\nconst emit = defineEmits([FOCUS, BLUR]);\n\nconst slots = useSlots();\nconst reveal = ref(false);\nconst isPassword = computed(() => props.type === DS_INPUT_TYPES.PASSWORD);\nconst resolvedType = computed(() =>\n isPassword.value && reveal.value ? DS_INPUT_TYPES.TEXT : props.type\n);\nconst hasSuffix = computed(\n () => !!slots.suffix || (props.error && props.showErrorIcon) || isPassword.value\n);\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n@use '../../assets/css/icons.scss' as i;\n\n.ds-input {\n display: block;\n width: 100%;\n\n &__wrap {\n position: relative;\n width: 100%;\n }\n\n &__control {\n box-sizing: border-box;\n width: 100%;\n padding: 0 16px;\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n font-weight: t.$weight-regular;\n line-height: 20px;\n\n background-color: colors.$white;\n\n /* Border: primary-main at rest → accent on focus (see precedence below). */\n border: 1px solid colors.$slate-main;\n border-radius: r.$radius-control;\n outline: none;\n\n transition: border-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n &.has-suffix {\n padding-right: 50px;\n }\n\n &:focus {\n border-color: colors.$accent-main;\n }\n\n &::placeholder {\n color: colors.$slate-soft;\n }\n }\n\n &__suffix {\n position: absolute;\n top: 0;\n right: 0;\n\n display: flex;\n align-items: center;\n gap: 4px;\n\n height: 100%;\n padding: 0 16px;\n\n color: colors.$slate-soft;\n }\n\n &__toggle {\n display: inline-flex;\n padding: 0;\n\n color: inherit;\n\n background: transparent;\n border: 0;\n\n cursor: pointer;\n }\n\n &__error-icon {\n color: colors.$error-main;\n }\n\n &__error {\n margin-top: 2px;\n\n color: colors.$error-main;\n font-family: t.$font-family;\n font-weight: t.$weight-bold;\n font-size: t.font-size('caption');\n line-height: 18px;\n }\n\n /* ---------- Sizes (DS input heights + icon sizes) ---------- */\n &--sm {\n .ds-input__control {\n height: 36px;\n }\n .material-icons {\n font-size: i.icon-size(20);\n }\n }\n &--md {\n .ds-input__control {\n height: 48px;\n }\n .material-icons {\n font-size: i.icon-size(24);\n }\n }\n\n /* ---------- States (precedence: rest → focus → disabled → error) ---------- */\n &.is-disabled .ds-input__control {\n color: colors.$slate-mid;\n background-color: colors.$slate-bg;\n border-color: colors.$slate-mid;\n cursor: not-allowed;\n }\n &.is-error .ds-input__control,\n &.is-error .ds-input__control:focus {\n border-color: colors.$error-main;\n }\n}\n</style>\n","<template>\n <div class=\"ds-textarea\" :class=\"{ 'is-error': error, 'is-disabled': disabled }\">\n <textarea\n :id=\"id || undefined\"\n v-model=\"model\"\n class=\"ds-textarea__control\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n :disabled=\"disabled\"\n :name=\"name || undefined\"\n :aria-invalid=\"error || undefined\"\n :aria-describedby=\"error && errorMessage && id ? `${id}-error` : undefined\"\n @focus=\"emit(FOCUS, $event)\"\n @blur=\"emit(BLUR, $event)\"\n />\n <p v-if=\"error && errorMessage\" :id=\"id ? `${id}-error` : undefined\" class=\"ds-textarea__error\">\n {{ errorMessage }}\n </p>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { FOCUS, BLUR } from '@shared/constants/events';\n\nconst model = defineModel<string>({ default: '' });\n\ndefineProps({\n placeholder: { type: String, default: '' },\n rows: { type: Number, default: 4 },\n disabled: Boolean,\n error: Boolean,\n errorMessage: { type: String, default: '' },\n name: { type: String, default: '' },\n id: { type: String, default: '' },\n});\n\nconst emit = defineEmits([FOCUS, BLUR]);\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n\n.ds-textarea {\n display: block;\n width: 100%;\n\n &__control {\n box-sizing: border-box;\n width: 100%;\n padding: 12px 16px;\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n line-height: 20px;\n\n background-color: colors.$white;\n\n border: 1px solid colors.$slate-main;\n border-radius: r.$radius-control;\n outline: none;\n\n resize: vertical;\n transition: border-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n &:focus {\n border-color: colors.$accent-main;\n }\n\n &::placeholder {\n color: colors.$slate-soft;\n }\n }\n\n &__error {\n margin-top: 2px;\n\n color: colors.$error-main;\n font-family: t.$font-family;\n font-weight: t.$weight-bold;\n font-size: t.font-size('caption');\n line-height: 18px;\n }\n\n &.is-disabled .ds-textarea__control {\n color: colors.$slate-mid;\n background-color: colors.$slate-bg;\n border-color: colors.$slate-mid;\n cursor: not-allowed;\n }\n &.is-error .ds-textarea__control,\n &.is-error .ds-textarea__control:focus {\n border-color: colors.$error-main;\n }\n}\n</style>\n","<template>\n <label class=\"ds-checkbox\" :class=\"{ 'is-disabled': disabled }\">\n <input\n ref=\"inputRef\"\n v-model=\"model\"\n type=\"checkbox\"\n class=\"ds-checkbox__native\"\n :disabled=\"disabled\"\n :name=\"name || undefined\"\n />\n <span\n class=\"ds-checkbox__box\"\n :class=\"{ 'is-active': model || indeterminate }\"\n aria-hidden=\"true\"\n >\n <svg\n v-if=\"indeterminate\"\n class=\"ds-checkbox__mark\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n >\n <line x1=\"4\" y1=\"8\" x2=\"12\" y2=\"8\" />\n </svg>\n <svg\n v-else-if=\"model\"\n class=\"ds-checkbox__mark\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n >\n <path d=\"M3.5 8.5l3 3 6-6\" />\n </svg>\n </span>\n <span v-if=\"$slots.default || label\" class=\"ds-checkbox__label\"\n ><slot>{{ label }}</slot></span\n >\n </label>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, watch } from 'vue';\n\nconst model = defineModel<boolean>({ default: false });\n\nconst props = defineProps({\n indeterminate: Boolean,\n disabled: Boolean,\n name: { type: String, default: '' },\n // Label text (DS `label` prop); a default slot overrides it.\n label: { type: String, default: '' },\n});\n\nconst inputRef = ref<HTMLInputElement | null>(null);\n\n// `indeterminate` is a DOM property, not an attribute — keep it in sync for a11y.\nfunction syncIndeterminate() {\n if (inputRef.value) inputRef.value.indeterminate = props.indeterminate;\n}\nonMounted(syncIndeterminate);\nwatch(() => props.indeterminate, syncIndeterminate);\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/spacing.scss' as s;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n\n.ds-checkbox {\n display: inline-flex;\n align-items: center;\n gap: s.spacing(1);\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n\n cursor: pointer;\n\n &.is-disabled {\n color: colors.$slate-soft;\n cursor: not-allowed;\n }\n\n /* Native control drives state + focus, visually replaced by the custom box. */\n &__native {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n }\n\n &__box {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n\n width: 18px;\n height: 18px;\n\n color: colors.$white;\n\n background-color: colors.$white;\n\n border: 1px solid colors.$slate-mid;\n border-radius: r.$radius-control;\n\n transition:\n background-color m.$duration-default m.$easing,\n border-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n &.is-active {\n background-color: colors.$accent-main;\n border-color: colors.$accent-main;\n }\n }\n\n /* Focus ring on the box when the (hidden) native input is focused. */\n &__native:focus-visible + &__box {\n outline: 2px solid colors.$accent-mid;\n outline-offset: 1px;\n }\n\n &__mark {\n width: 14px;\n height: 14px;\n }\n\n &__label {\n line-height: 1.3;\n }\n}\n</style>\n","// DsAlert — the DS inline banner (Alert.jsx). type × effect (light tint / dark solid).\n\nexport const DS_ALERT_TYPES = {\n SUCCESS: 'success',\n INFO: 'info',\n WARNING: 'warning',\n ERROR: 'error',\n} as const;\nexport type DsAlertType = (typeof DS_ALERT_TYPES)[keyof typeof DS_ALERT_TYPES];\n\nexport const DS_ALERT_EFFECTS = {\n LIGHT: 'light', // tinted: family tint bg + mid border + shadow text\n DARK: 'dark', // solid: family shadow bg + white text\n} as const;\nexport type DsAlertEffect = (typeof DS_ALERT_EFFECTS)[keyof typeof DS_ALERT_EFFECTS];\n\n// Material Icon per type (leading icon when showIcon).\nexport const DS_ALERT_ICONS: Record<DsAlertType, string> = {\n success: 'check_circle',\n info: 'info',\n warning: 'warning',\n error: 'error',\n};\n","<template>\n <div\n v-if=\"visible\"\n class=\"ds-alert\"\n :class=\"[`ds-alert--${type}`, `ds-alert--${effect}`, { 'is-center': center }]\"\n role=\"alert\"\n >\n <i v-if=\"showIcon\" class=\"material-icons ds-alert__icon\" aria-hidden=\"true\">{{ icon }}</i>\n\n <div class=\"ds-alert__content\">\n <div v-if=\"title\" class=\"ds-alert__title\" :class=\"{ 'has-body': hasBody }\">{{ title }}</div>\n <div v-if=\"hasBody\" class=\"ds-alert__body\">\n <slot>{{ description }}</slot>\n </div>\n </div>\n\n <button\n v-if=\"closable\"\n type=\"button\"\n class=\"ds-alert__close\"\n :aria-label=\"closeLabel || undefined\"\n @click=\"onClose\"\n >\n <i class=\"material-icons\" aria-hidden=\"true\">close</i>\n </button>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, useSlots, type PropType } from 'vue';\nimport { CLOSE } from '@shared/constants/events';\nimport {\n DS_ALERT_TYPES,\n DS_ALERT_EFFECTS,\n DS_ALERT_ICONS,\n type DsAlertType,\n type DsAlertEffect,\n} from './DsAlert.types';\n\nconst props = defineProps({\n type: { type: String as PropType<DsAlertType>, default: DS_ALERT_TYPES.INFO },\n effect: { type: String as PropType<DsAlertEffect>, default: DS_ALERT_EFFECTS.LIGHT },\n title: { type: String, default: '' },\n description: { type: String, default: '' },\n showIcon: Boolean,\n closable: Boolean,\n center: Boolean,\n closeLabel: { type: String, default: '' },\n});\n\nconst emit = defineEmits([CLOSE]);\n\nconst slots = useSlots();\nconst visible = ref(true);\nconst icon = computed(() => DS_ALERT_ICONS[props.type]);\nconst hasBody = computed(() => !!slots.default || props.description.length > 0);\n\nfunction onClose() {\n visible.value = false;\n emit(CLOSE);\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/icons.scss' as i;\n\n.ds-alert {\n position: relative;\n\n display: flex;\n align-items: center;\n\n padding: 8px 16px;\n\n font-family: t.$font-family;\n\n border: 1px solid transparent;\n border-radius: r.$radius-control;\n\n &.is-center {\n justify-content: center;\n }\n\n &__icon {\n margin-right: 8px;\n // Status glyph uses the DS default icon size (md-20) for presence next to the\n // 13px title; the close ✕ below stays at 14 (discreet secondary action).\n font-size: i.icon-size(20);\n }\n\n &__content {\n flex: 1;\n padding: 0 8px;\n }\n\n &__title {\n font-size: t.font-size('small');\n font-weight: t.$weight-regular;\n line-height: 18px;\n\n &.has-body {\n font-weight: t.$weight-bold;\n }\n }\n\n &__body {\n margin-top: 5px;\n font-size: t.font-size('caption');\n }\n\n &__title.has-body + .ds-alert__body {\n margin-top: 5px;\n }\n\n &__close {\n display: inline-flex;\n padding: 0;\n\n color: inherit;\n\n background: transparent;\n border: 0;\n\n cursor: pointer;\n\n .material-icons {\n font-size: i.icon-size(14);\n }\n }\n\n /* ---------- light (tinted) — bg tint + mid border + shadow text ---------- */\n &--light {\n &.ds-alert--success {\n color: colors.$success-shadow;\n border-color: colors.$success-mid;\n background-color: colors.$success-tint;\n }\n &.ds-alert--info {\n color: colors.$info-shadow;\n border-color: colors.$info-mid;\n background-color: colors.$info-tint;\n }\n &.ds-alert--warning {\n color: colors.$warning-shadow;\n border-color: colors.$warning-mid;\n background-color: colors.$warning-tint;\n }\n &.ds-alert--error {\n color: colors.$error-shadow;\n border-color: colors.$error-mid;\n background-color: colors.$error-tint;\n }\n }\n\n /* ---------- dark (solid) — bg shadow + white text ---------- */\n &--dark {\n color: colors.$white;\n border-color: transparent;\n\n &.ds-alert--success {\n background-color: colors.$success-shadow;\n }\n &.ds-alert--info {\n background-color: colors.$info-shadow;\n }\n &.ds-alert--warning {\n background-color: colors.$warning-shadow;\n }\n &.ds-alert--error {\n background-color: colors.$error-shadow;\n }\n }\n}\n</style>\n","// DsNotification — the DS toast card (Notification.jsx). Fixed-position, 3 types.\n\nexport const DS_NOTIFICATION_TYPES = {\n INFO: 'info',\n WARNING: 'warning',\n ERROR: 'error',\n} as const;\nexport type DsNotificationType = (typeof DS_NOTIFICATION_TYPES)[keyof typeof DS_NOTIFICATION_TYPES];\n\nexport const DS_NOTIFICATION_POSITIONS = {\n TOP_LEFT: 'top-left',\n TOP_RIGHT: 'top-right',\n BOTTOM_LEFT: 'bottom-left',\n BOTTOM_RIGHT: 'bottom-right',\n} as const;\nexport type DsNotificationPosition =\n (typeof DS_NOTIFICATION_POSITIONS)[keyof typeof DS_NOTIFICATION_POSITIONS];\n\n// Material Icon per type (Notification.jsx).\nexport const DS_NOTIFICATION_ICONS: Record<DsNotificationType, string> = {\n info: 'mode_edit',\n warning: 'warning',\n error: 'warning',\n};\n","<template>\n <Teleport to=\"body\">\n <div v-if=\"visible\" class=\"ds-notification\" :class=\"`ds-notification--${position}`\">\n <div class=\"ds-notification__card\" :class=\"`ds-notification__card--${type}`\" role=\"alert\">\n <i class=\"material-icons ds-notification__icon\" aria-hidden=\"true\">{{ icon }}</i>\n\n <div class=\"ds-notification__content\">\n <span v-if=\"title\" class=\"ds-notification__title\">{{ title }}</span>\n <p v-if=\"description\" class=\"ds-notification__desc\">{{ description }}</p>\n <a\n v-if=\"linkTitle\"\n class=\"ds-notification__link\"\n :href=\"link || undefined\"\n target=\"_blank\"\n rel=\"noreferrer\"\n >{{ linkTitle }}</a\n >\n </div>\n\n <button\n v-if=\"showClose\"\n type=\"button\"\n class=\"ds-notification__close\"\n :aria-label=\"closeLabel || undefined\"\n @click=\"onClose\"\n >\n <i class=\"material-icons\" aria-hidden=\"true\">close</i>\n </button>\n </div>\n </div>\n </Teleport>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, type PropType } from 'vue';\nimport { CLOSE } from '@shared/constants/events';\nimport {\n DS_NOTIFICATION_TYPES,\n DS_NOTIFICATION_POSITIONS,\n DS_NOTIFICATION_ICONS,\n type DsNotificationType,\n type DsNotificationPosition,\n} from './DsNotification.types';\n\nconst props = defineProps({\n type: { type: String as PropType<DsNotificationType>, default: DS_NOTIFICATION_TYPES.INFO },\n position: {\n type: String as PropType<DsNotificationPosition>,\n default: DS_NOTIFICATION_POSITIONS.BOTTOM_LEFT,\n },\n title: { type: String, default: '' },\n description: { type: String, default: '' },\n linkTitle: { type: String, default: '' },\n link: { type: String, default: '' },\n showClose: { type: Boolean, default: true },\n closeLabel: { type: String, default: '' },\n});\n\nconst emit = defineEmits([CLOSE]);\n\nconst visible = ref(true);\nconst icon = computed(() => DS_NOTIFICATION_ICONS[props.type]);\n\nfunction onClose() {\n visible.value = false;\n emit(CLOSE);\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/shadow.scss' as sh;\n@use '../../assets/css/icons.scss' as i;\n\n.ds-notification {\n position: fixed;\n z-index: 2000;\n\n &--top-left {\n top: 0;\n left: 0;\n }\n &--top-right {\n top: 0;\n right: 0;\n }\n &--bottom-left {\n bottom: 0;\n left: 0;\n }\n &--bottom-right {\n bottom: 0;\n right: 0;\n }\n\n &__card {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n\n max-width: 331px;\n margin: 24px;\n padding: 16px;\n\n border: 1px solid transparent;\n border-radius: r.$radius-card-lg;\n\n box-shadow: sh.$shadow-soft;\n\n &--info {\n background-color: colors.$info-tint;\n border-color: rgba(colors.$info-main, 0.7);\n\n .ds-notification__icon {\n color: colors.$info-main;\n }\n }\n &--warning {\n background-color: colors.$warning-tint;\n border-color: rgba(colors.$warning-main, 0.7);\n\n .ds-notification__icon {\n color: colors.$warning-main;\n }\n }\n &--error {\n background-color: colors.$error-tint;\n border-color: rgba(colors.$error-main, 0.7);\n\n .ds-notification__icon {\n color: colors.$error-main;\n }\n }\n }\n\n &__icon {\n font-size: i.icon-size(20);\n }\n\n &__content {\n display: flex;\n flex-direction: column;\n\n color: colors.$slate-shadow;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n line-height: 21px;\n }\n\n &__title {\n font-weight: t.$weight-bold;\n }\n\n &__desc {\n margin: 0;\n padding-top: 8px;\n }\n\n &__link {\n padding-top: 8px;\n\n color: colors.$accent-main;\n text-decoration: underline;\n }\n\n &__close {\n display: inline-flex;\n margin-left: auto;\n padding: 0;\n\n color: colors.$slate-soft;\n\n background: transparent;\n border: 0;\n\n cursor: pointer;\n\n .material-icons {\n font-size: i.icon-size(20);\n }\n }\n}\n</style>\n","<template>\n <Teleport to=\"body\">\n <div v-if=\"open\" class=\"ds-modal\" @click=\"onBackdrop\">\n <div\n ref=\"dialogRef\"\n class=\"ds-modal__dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"title || 'dialog'\"\n tabindex=\"-1\"\n :style=\"{ width }\"\n @click.stop\n >\n <div v-if=\"title || showClose\" class=\"ds-modal__header\">\n <span class=\"ds-modal__title\">{{ title }}</span>\n <DsButton\n v-if=\"showClose\"\n variant=\"tertiary\"\n circle\n left-icon=\"close\"\n :aria-label=\"closeLabel || undefined\"\n @click=\"emit(CLOSE)\"\n />\n </div>\n\n <div class=\"ds-modal__body\"><slot /></div>\n\n <div v-if=\"$slots.footer\" class=\"ds-modal__footer\"><slot name=\"footer\" /></div>\n </div>\n </div>\n </Teleport>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, watch, nextTick, onBeforeUnmount } from 'vue';\nimport { DsButton } from '@shared/ui/DsButton';\nimport { CLOSE } from '@shared/constants/events';\n\nconst props = defineProps({\n open: Boolean,\n title: { type: String, default: '' },\n showClose: { type: Boolean, default: true },\n width: { type: String, default: '440px' },\n closeOnBackdrop: { type: Boolean, default: true },\n closeLabel: { type: String, default: '' },\n});\n\nconst emit = defineEmits([CLOSE]);\n\nconst dialogRef = ref<HTMLElement | null>(null);\nlet lastFocused: HTMLElement | null = null;\n\nfunction onBackdrop() {\n if (props.closeOnBackdrop) emit(CLOSE);\n}\n\nfunction focusables(): HTMLElement[] {\n if (!dialogRef.value) return [];\n const sel =\n 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n return Array.from(dialogRef.value.querySelectorAll<HTMLElement>(sel)).filter(\n (el) => el.offsetParent !== null\n );\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'Escape') {\n emit(CLOSE);\n return;\n }\n if (e.key !== 'Tab') return;\n const f = focusables();\n if (f.length === 0) {\n e.preventDefault();\n dialogRef.value?.focus();\n return;\n }\n const first = f[0];\n const last = f[f.length - 1];\n if (e.shiftKey && document.activeElement === first) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && document.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n}\n\nwatch(\n () => props.open,\n (open) => {\n if (typeof document === 'undefined') return;\n if (open) {\n lastFocused = document.activeElement as HTMLElement;\n document.addEventListener('keydown', onKeydown);\n document.body.style.overflow = 'hidden';\n nextTick(() => (focusables()[0] ?? dialogRef.value)?.focus());\n } else {\n document.removeEventListener('keydown', onKeydown);\n document.body.style.overflow = '';\n lastFocused?.focus?.();\n }\n },\n { immediate: true }\n);\n\nonBeforeUnmount(() => {\n if (typeof document === 'undefined') return;\n document.removeEventListener('keydown', onKeydown);\n document.body.style.overflow = '';\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/shadow.scss' as sh;\n\n.ds-modal {\n position: fixed;\n inset: 0;\n z-index: 2001;\n\n display: flex;\n align-items: flex-start;\n justify-content: center;\n\n padding: 15vh 16px 50px;\n overflow: auto;\n\n background: colors.$backdrop; // grey-10 @ 50%, no blur\n\n &__dialog {\n max-width: 100%;\n\n background: colors.$white;\n\n border-radius: r.$radius-card;\n box-shadow: sh.$shadow-card;\n\n padding: 8px;\n\n outline: none;\n }\n\n &__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n min-height: 36px;\n padding: 8px 0 8px 12px;\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n font-weight: t.$weight-bold;\n\n border-bottom: 1px solid colors.$slate-tint;\n }\n\n &__body {\n padding: 20px;\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n }\n\n &__footer {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n\n padding: 8px;\n\n border-top: 1px solid colors.$slate-tint;\n }\n}\n</style>\n","// DsVerificationIcon — status glyph for a contact data point.\n// 1:1 with the DS source (components/data/VerificationIcon.jsx):\n// verified === true -> \"verified\" (info-main, blue)\n// verified === false + email -> \"dangerous\" (error-main, coral)\n// verified === false + phone -> \"error\" (warning-main, orange)\n// verified == null (new) -> \"new_releases\" (slate-soft, grey)\n// `isContactData` flags emails (coral) vs phones (orange) when unverified.\n\nexport const DS_VERIFICATION_STATES = {\n VERIFIED: 'verified',\n UNVERIFIED: 'unverified',\n NEW: 'new',\n} as const;\nexport type DsVerificationState =\n (typeof DS_VERIFICATION_STATES)[keyof typeof DS_VERIFICATION_STATES];\n","<template>\n <i\n class=\"material-icons ds-verification-icon\"\n :class=\"[\n `ds-verification-icon--${status.state}`,\n { 'ds-verification-icon--email': isContactData },\n ]\"\n :style=\"{ fontSize: `${size}px` }\"\n role=\"img\"\n :aria-label=\"label || status.label\"\n :title=\"label || status.label\"\n >\n <slot>{{ status.icon }}</slot>\n </i>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, type PropType } from 'vue';\nimport { DS_VERIFICATION_STATES } from './DsVerificationIcon.types';\n\nconst props = defineProps({\n // true = verified · false = not verified · null/undefined = newly found.\n verified: {\n type: Boolean as PropType<boolean | null>,\n default: null,\n },\n // Emails (true) use the coral \"dangerous\" glyph when unverified; phones use orange.\n isContactData: {\n type: Boolean,\n default: false,\n },\n size: {\n type: Number,\n default: 20,\n },\n // Optional override for the accessible label / tooltip (the app owns i18n).\n // Empty → the English default derived from the verification state.\n label: {\n type: String,\n default: '',\n },\n});\n\nconst status = computed(() => {\n if (props.verified === null || props.verified === undefined) {\n return {\n state: DS_VERIFICATION_STATES.NEW,\n icon: 'new_releases',\n label: 'Newly found',\n };\n }\n if (props.verified) {\n return {\n state: DS_VERIFICATION_STATES.VERIFIED,\n icon: 'verified',\n label: 'Verified',\n };\n }\n return {\n state: DS_VERIFICATION_STATES.UNVERIFIED,\n icon: props.isContactData ? 'dangerous' : 'error',\n label: 'Not verified',\n };\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n\n.ds-verification-icon {\n line-height: 1;\n\n // States map DS colors (VerificationIcon.jsx). isContactData splits the\n // unverified color into coral (email) vs orange (phone).\n &--new {\n color: colors.$slate-soft; // --primary-secondary-text\n }\n &--verified {\n color: colors.$info-main;\n }\n &--unverified {\n color: colors.$warning-main; // phone (orange)\n\n &.ds-verification-icon--email {\n color: colors.$error-main; // email (coral \"dangerous\")\n }\n }\n}\n</style>\n","<template>\n <div class=\"ds-contact-value\" :class=\"{ 'ds-contact-value--main': main }\">\n <DsVerificationIcon :verified=\"verified\" :is-contact-data=\"isContactData\" />\n\n <span class=\"ds-contact-value__value\" :title=\"value\">{{ value }}</span>\n\n <!-- Marquee slot: badge and actions share one grid cell and cross-fade/slide\n on hover (or keyboard focus-within). -->\n <span class=\"ds-contact-value__marquee\">\n <DsBadge\n v-if=\"valueType\"\n class=\"ds-contact-value__badge\"\n :tone=\"main ? DS_BADGE_TONES.ACCENT : DS_BADGE_TONES.GREY\"\n aria-hidden=\"true\"\n >{{ valueType }}</DsBadge\n >\n\n <span class=\"ds-contact-value__actions\">\n <button\n type=\"button\"\n class=\"ds-contact-value__action\"\n :class=\"{ 'is-copied': copied }\"\n :aria-label=\"copied ? copiedLabel : copyLabel\"\n :title=\"copied ? copiedLabel : copyLabel\"\n @click=\"handleCopy\"\n >\n <i class=\"material-icons\" aria-hidden=\"true\">{{ copied ? 'check' : 'content_copy' }}</i>\n </button>\n\n <template v-if=\"showRate\">\n <button\n type=\"button\"\n class=\"ds-contact-value__action\"\n :class=\"{ 'is-active': correct === true }\"\n :aria-label=\"correctLabel\"\n :aria-pressed=\"correct === true\"\n :title=\"correctLabel\"\n @click=\"handleCorrect\"\n >\n <i\n :class=\"correct === true ? 'material-icons' : 'material-icons-outlined'\"\n aria-hidden=\"true\"\n >thumb_up</i\n >\n </button>\n <button\n type=\"button\"\n class=\"ds-contact-value__action\"\n :class=\"{ 'is-active': correct === false }\"\n :aria-label=\"incorrectLabel\"\n :aria-pressed=\"correct === false\"\n :title=\"incorrectLabel\"\n @click=\"handleIncorrect\"\n >\n <i\n :class=\"correct === false ? 'material-icons' : 'material-icons-outlined'\"\n aria-hidden=\"true\"\n >thumb_down</i\n >\n </button>\n </template>\n </span>\n </span>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onBeforeUnmount, type PropType } from 'vue';\nimport { DsVerificationIcon } from '@shared/ui/DsVerificationIcon';\nimport { DsBadge, DS_BADGE_TONES } from '@shared/ui/DsBadge';\nimport { COPY, CORRECT, INCORRECT } from '@shared/constants/events';\n\nconst props = defineProps({\n value: { type: String, required: true },\n valueType: { type: String, default: '' },\n verified: { type: Boolean as PropType<boolean | null>, default: null },\n main: { type: Boolean, default: false },\n isContactData: { type: Boolean, default: false },\n showRate: { type: Boolean, default: false },\n correct: { type: Boolean as PropType<boolean | null>, default: null },\n // User-facing labels (the app owns i18n); English DS defaults.\n copyLabel: { type: String, default: 'Copy' },\n copiedLabel: { type: String, default: 'Copied!' },\n correctLabel: { type: String, default: 'Correct' },\n incorrectLabel: { type: String, default: 'Incorrect' },\n});\n\n// Runtime array so the emit names are registered at runtime (the type-only\n// form with `typeof CONST` compiles to an empty `emits`, which lets listeners\n// fall through as native attrs). Names come from the shared events constants.\nconst emit = defineEmits([COPY, CORRECT, INCORRECT]);\n\nconst copied = ref(false);\nlet copiedTimer: ReturnType<typeof setTimeout> | undefined;\n\nfunction handleCopy() {\n if (typeof navigator !== 'undefined' && navigator.clipboard) {\n navigator.clipboard.writeText(props.value).catch(() => {});\n }\n copied.value = true;\n clearTimeout(copiedTimer);\n copiedTimer = setTimeout(() => {\n copied.value = false;\n }, 1200);\n emit(COPY, props.value);\n}\n\nfunction handleCorrect() {\n emit(CORRECT);\n}\nfunction handleIncorrect() {\n emit(INCORRECT);\n}\n\nonBeforeUnmount(() => clearTimeout(copiedTimer));\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n@use '../../assets/css/icons.scss' as i;\n\n.ds-contact-value {\n display: flex;\n align-items: center;\n\n padding: 6px; // DS ContactValue.jsx\n border-radius: r.$radius-control;\n\n background: transparent;\n transition: background-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n &:hover,\n &:focus-within {\n background-color: colors.$slate-bg; // --primary-background\n }\n\n &--main:hover,\n &--main:focus-within {\n background-color: colors.$accent-bg;\n }\n\n // -- value ---------------------------------------------------------------\n &__value {\n flex: 1;\n min-width: 0;\n margin: 0 8px; // DS\n\n color: colors.$slate-main; // --primary-main\n font-family: t.$font-family;\n font-size: t.font-size('body');\n\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n // -- marquee (badge + actions share one cell) ----------------------------\n &__marquee {\n display: inline-grid;\n align-items: center;\n justify-items: end;\n margin-left: auto;\n }\n\n &__badge,\n &__actions {\n grid-area: 1 / 1;\n transition:\n transform m.$duration-reveal m.$easing,\n opacity m.$duration-reveal m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n }\n\n // At rest: badge in place, actions parked to the right and hidden.\n &__badge {\n transform: translateX(0);\n opacity: 1;\n }\n &__actions {\n display: inline-flex;\n align-items: center;\n gap: 8px; // DS\n\n transform: translateX(8px);\n opacity: 0;\n pointer-events: none;\n }\n\n // On hover / keyboard focus: badge slides out, actions slide in.\n &:hover &__badge,\n &:focus-within &__badge {\n transform: translateX(8px);\n opacity: 0;\n }\n &:hover &__actions,\n &:focus-within &__actions {\n transform: translateX(0);\n opacity: 1;\n pointer-events: auto;\n }\n\n // -- action buttons ------------------------------------------------------\n &__action {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n padding: 0;\n border: none;\n background: none;\n cursor: pointer;\n\n color: colors.$slate-soft; // --primary-secondary-text\n\n .material-icons,\n .material-icons-outlined {\n font-size: i.icon-size(20);\n line-height: 1;\n }\n\n &.is-copied {\n color: colors.$success-shadow;\n }\n }\n}\n</style>\n","<template>\n <section class=\"ds-data-card\" :aria-label=\"title\">\n <!-- header -->\n <div\n class=\"ds-data-card__header\"\n :class=\"{ 'is-collapsed': isCollapsed, 'is-collapsible': collapsible }\"\n @click=\"collapsible && toggle()\"\n >\n <div class=\"ds-data-card__heading\">\n <i class=\"material-icons ds-data-card__icon\" aria-hidden=\"true\">{{ icon }}</i>\n <span class=\"ds-data-card__title\">{{ title }}</span>\n </div>\n\n <div class=\"ds-data-card__actions\" @click.stop>\n <button\n v-if=\"!isCollapsed && showEdit\"\n type=\"button\"\n class=\"ds-data-card__edit\"\n @click=\"onEdit\"\n >\n {{ editLabel }}\n </button>\n\n <DsButton\n v-if=\"!isCollapsed && showSort\"\n variant=\"tertiary\"\n size=\"medium\"\n circle\n left-icon=\"swap_vert\"\n :aria-label=\"sortLabel\"\n @click=\"onSort\"\n />\n\n <DsButton\n v-if=\"collapsible\"\n variant=\"tertiary\"\n size=\"medium\"\n circle\n :left-icon=\"collapsed ? 'keyboard_arrow_down' : 'keyboard_arrow_up'\"\n :aria-label=\"toggleLabel\"\n @click=\"toggle\"\n />\n\n <button\n v-if=\"showClose\"\n type=\"button\"\n class=\"ds-data-card__close\"\n :aria-label=\"closeLabel\"\n @click=\"onClose\"\n >\n <i class=\"material-icons\" aria-hidden=\"true\">close</i>\n </button>\n </div>\n </div>\n\n <!-- body -->\n <div v-if=\"!isCollapsed\" class=\"ds-data-card__body\">\n <div v-if=\"empty\" class=\"ds-data-card__empty\">\n <i class=\"material-icons-outlined ds-data-card__empty-icon\" aria-hidden=\"true\">{{\n icon\n }}</i>\n <span>{{ emptyLabel }}</span>\n </div>\n\n <template v-else>\n <div v-if=\"mostLikely\">\n <div v-if=\"showSections\" class=\"ds-data-card__section\">{{ mostLikelyLabel }}</div>\n <DsContactValue\n :value=\"mostLikely.value\"\n :value-type=\"mostLikely.type\"\n :verified=\"mostLikely.verified\"\n :correct=\"mostLikely.correct\"\n main\n :is-contact-data=\"isContactData\"\n :show-rate=\"showRate\"\n @copy=\"onCopy\"\n @correct=\"onCorrect(mostLikely)\"\n @incorrect=\"onIncorrect(mostLikely)\"\n />\n </div>\n\n <div v-if=\"more.length > 0\">\n <div v-if=\"showSections\" class=\"ds-data-card__section\">{{ moreLabel }}</div>\n <div class=\"ds-data-card__more\">\n <DsContactValue\n v-for=\"datum in more\"\n :key=\"datum.value\"\n :value=\"datum.value\"\n :value-type=\"datum.type\"\n :verified=\"datum.verified\"\n :correct=\"datum.correct\"\n :is-contact-data=\"isContactData\"\n :show-rate=\"showRate\"\n @copy=\"onCopy\"\n @correct=\"onCorrect(datum)\"\n @incorrect=\"onIncorrect(datum)\"\n />\n </div>\n </div>\n </template>\n </div>\n </section>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, type PropType } from 'vue';\nimport { DsButton } from '@shared/ui/DsButton';\nimport { DsContactValue } from '@shared/ui/DsContactValue';\nimport { COPY, SORT, EDIT, CLOSE, CORRECT, INCORRECT } from '@shared/constants/events';\nimport type { DsContactDatum } from './DsDataCard.types';\n\nconst props = defineProps({\n title: { type: String, required: true },\n // Material Icon name for the header (e.g. \"email\", \"call\").\n icon: { type: String, default: 'email' },\n mostLikely: { type: Object as PropType<DsContactDatum | null>, default: null },\n more: { type: Array as PropType<DsContactDatum[]>, default: () => [] },\n collapsible: { type: Boolean, default: false },\n showSort: { type: Boolean, default: true },\n showEdit: { type: Boolean, default: false },\n showClose: { type: Boolean, default: false },\n isContactData: { type: Boolean, default: false },\n showRate: { type: Boolean, default: false },\n // User-facing labels (the app owns i18n); English DS defaults.\n editLabel: { type: String, default: 'Edit' },\n emptyLabel: { type: String, default: 'No data found' },\n mostLikelyLabel: { type: String, default: 'MOST LIKELY' },\n moreLabel: { type: String, default: 'MORE' },\n sortLabel: { type: String, default: 'Sort' },\n toggleLabel: { type: String, default: 'Toggle' },\n closeLabel: { type: String, default: 'Close' },\n});\n\n// Runtime array so the emit names are registered at runtime (the type-only\n// form with `typeof CONST` compiles to an empty `emits`, which lets listeners\n// fall through as native attrs). Names come from the shared events constants.\nconst emit = defineEmits([COPY, SORT, EDIT, CLOSE, CORRECT, INCORRECT]);\n\nconst collapsed = ref(false);\n\nconst total = computed(() => props.more.length + (props.mostLikely ? 1 : 0));\nconst showSections = computed(() => total.value > 3);\nconst empty = computed(() => total.value === 0);\nconst isCollapsed = computed(() => props.collapsible && collapsed.value);\n\nfunction toggle() {\n collapsed.value = !collapsed.value;\n}\nfunction onCopy(value: string) {\n emit(COPY, value);\n}\nfunction onSort() {\n emit(SORT);\n}\nfunction onEdit() {\n emit(EDIT);\n}\nfunction onClose() {\n emit(CLOSE);\n}\nfunction onCorrect(datum: DsContactDatum) {\n emit(CORRECT, datum);\n}\nfunction onIncorrect(datum: DsContactDatum) {\n emit(INCORRECT, datum);\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/shadow.scss' as sh;\n@use '../../assets/css/icons.scss' as i;\n\n.ds-data-card {\n display: block;\n\n padding: 8px;\n\n background-color: colors.$white;\n border-radius: r.$radius-card;\n box-shadow: sh.$shadow-card;\n\n // -- header --------------------------------------------------------------\n &__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n padding: 6px 6px 14px; // DS DataCard.jsx\n border-bottom: 1px solid colors.$slate-tint; // --primary-tint\n\n &.is-collapsible {\n cursor: pointer;\n }\n &.is-collapsed {\n border-bottom: none;\n }\n }\n\n &__heading {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n }\n\n &__icon {\n font-size: i.icon-size(20);\n line-height: 1;\n color: colors.$slate-main; // --primary-main\n }\n\n &__title {\n font-family: t.$font-family;\n font-weight: t.$weight-bold;\n font-size: t.font-size('body');\n color: colors.$slate-main;\n\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n &__actions {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n &__edit {\n padding: 0;\n border: none;\n background: none;\n cursor: pointer;\n\n font-family: t.$font-family;\n font-weight: t.$weight-bold;\n font-size: t.font-size('caption');\n color: colors.$accent-main;\n }\n\n &__close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n padding: 0;\n border: none;\n background: none;\n cursor: pointer;\n\n color: colors.$slate-soft; // --primary-secondary-text\n\n .material-icons {\n font-size: i.icon-size(20);\n line-height: 1;\n }\n }\n\n // -- body ----------------------------------------------------------------\n &__body {\n padding-top: 8px;\n }\n\n &__section {\n padding: 6px;\n\n font-family: t.$font-family;\n font-size: t.font-size('micro');\n line-height: 18px;\n letter-spacing: 0.04em;\n color: colors.$slate-main;\n }\n\n &__more {\n display: flex;\n flex-direction: column;\n gap: 8px;\n\n max-height: 132px; // DS: ~3 rows before the \"MORE\" list scrolls\n overflow: auto;\n }\n\n &__empty {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px;\n\n font-family: t.$font-family;\n font-size: t.font-size('body');\n color: colors.$slate-main;\n }\n\n &__empty-icon {\n font-size: i.icon-size(20);\n line-height: 1;\n color: colors.$slate-soft;\n }\n}\n</style>\n"],"names":["DS_SPINNER_TONES","_createElementBlock","_normalizeClass","__props","_normalizeStyle","_hoisted_2","_toDisplayString","CLICK","CLOSE","FOCUS","BLUR","COPY","CORRECT","INCORRECT","SORT","EDIT","DS_BUTTON_VARIANTS","DS_BUTTON_SIZES","DS_BUTTON_NATIVE_TYPES","props","emit","__emit","iconSize","computed","spinnerTone","handleClick","event","_createBlock","_unref","DsSpinner","$slots","_openBlock","_renderSlot","_ctx","_createElementVNode","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","DS_TAG_TONES","_cache","$event","DS_BADGE_TONES","initials","ariaLabel","boxStyle","hashed","seed","hash","i","rgb","light","DS_TABLE_ALIGN","_hoisted_1","_Fragment","_renderList","column","_a","row","DS_INPUT_TYPES","DS_INPUT_SIZES","model","_useModel","slots","useSlots","reveal","ref","isPassword","resolvedType","hasSuffix","_hoisted_7","inputRef","syncIndeterminate","onMounted","watch","DS_ALERT_TYPES","DS_ALERT_EFFECTS","DS_ALERT_ICONS","visible","icon","hasBody","onClose","DS_NOTIFICATION_TYPES","DS_NOTIFICATION_POSITIONS","DS_NOTIFICATION_ICONS","_Teleport","dialogRef","lastFocused","onBackdrop","focusables","el","onKeydown","e","first","last","open","nextTick","_b","onBeforeUnmount","DsButton","DS_VERIFICATION_STATES","status","_createTextVNode","copied","copiedTimer","handleCopy","handleCorrect","handleIncorrect","_createVNode","DsVerificationIcon","DsBadge","collapsed","total","showSections","empty","isCollapsed","toggle","onCopy","value","onSort","onEdit","onCorrect","datum","onIncorrect","_hoisted_8","_hoisted_9","_hoisted_10","DsContactValue","_hoisted_11","_hoisted_12","_hoisted_13"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.es.js","sources":["../src/ui/DsSpinner/DsSpinner.types.ts","../src/ui/DsSpinner/DsSpinner.vue","../src/constants/events.ts","../src/ui/DsButton/DsButton.types.ts","../src/ui/DsButton/DsButton.vue","../src/ui/DsTag/DsTag.types.ts","../src/ui/DsTag/DsTag.vue","../src/ui/DsBadge/DsBadge.types.ts","../src/ui/DsBadge/DsBadge.vue","../src/ui/DsAvatar/DsAvatar.vue","../src/ui/DsCheckbox/DsCheckbox.vue","../src/ui/DsTable/DsTable.types.ts","../src/ui/DsTable/DsTable.vue","../src/ui/DsInput/DsInput.types.ts","../src/ui/DsInput/DsInput.vue","../src/ui/DsTextarea/DsTextarea.types.ts","../src/ui/DsTextarea/DsTextarea.vue","../src/ui/DsAlert/DsAlert.types.ts","../src/ui/DsAlert/DsAlert.vue","../src/ui/DsNotification/DsNotification.types.ts","../src/ui/DsNotification/DsNotification.vue","../src/ui/DsModal/DsModal.vue","../src/ui/DsTooltip/DsTooltip.types.ts","../src/ui/DsTooltip/DsTooltip.vue","../src/ui/DsVerificationIcon/DsVerificationIcon.types.ts","../src/ui/DsVerificationIcon/DsVerificationIcon.vue","../src/ui/DsContactValue/DsContactValue.vue","../src/ui/DsDataCard/DsDataCard.vue","../src/ui/DsRadio/DsRadio.vue","../src/ui/DsSwitch/DsSwitch.vue","../src/ui/DsItemSelectorButton/DsItemSelectorButton.vue","../src/ui/DsSelect/DsSelect.types.ts","../src/ui/DsSelect/DsSelect.vue","../src/ui/DsRow/DsRow.vue","../src/ui/DsCell/DsCell.vue","../src/ui/DsCustomTag/DsCustomTag.types.ts","../src/ui/DsCustomTag/DsCustomTag.vue","../src/ui/DsPill/DsPill.vue","../src/ui/DsPillWithBorder/DsPillWithBorder.vue","../src/ui/DsSegmentedProgressBar/DsSegmentedProgressBar.vue","../src/ui/DsEmptyState/DsEmptyState.vue","../src/ui/DsPaginator/DsPaginator.vue","../src/ui/DsMenuItem/DsMenuItem.vue","../src/ui/DsWizardStepper/DsWizardStepper.types.ts","../src/ui/DsWizardStepper/DsWizardStepper.vue"],"sourcesContent":["// DsSpinner — the DS \"Spinner\": a rotating border-ring. Tones mirror Spinner.jsx.\n\nexport const DS_SPINNER_TONES = {\n DARK: 'dark', // slate ring — for light surfaces (default)\n LIGHT: 'light', // white ring — for dark surfaces / solid buttons\n ACCENT: 'accent', // accent ring\n} as const;\nexport type DsSpinnerTone = (typeof DS_SPINNER_TONES)[keyof typeof DS_SPINNER_TONES];\n\n// Named sizes (DS `size`: number | \"small\" | \"medium\" | \"large\") — resolved to\n// px by DsSpinner.vue. Numeric sizes still pass straight through; the default\n// (24) is unchanged and doesn't correspond to any of these named steps.\nexport const DS_SPINNER_SIZES = {\n SMALL: 'small', // 20px\n MEDIUM: 'medium', // 32px\n LARGE: 'large', // 48px\n} as const;\nexport type DsSpinnerSize = (typeof DS_SPINNER_SIZES)[keyof typeof DS_SPINNER_SIZES];\n","<template>\n <span\n class=\"ds-spinner\"\n :class=\"`ds-spinner--${tone}`\"\n role=\"status\"\n :style=\"{ width: `${resolvedSize}px`, height: `${resolvedSize}px` }\"\n :aria-label=\"label || undefined\"\n ></span>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, type PropType } from 'vue';\nimport {\n DS_SPINNER_TONES,\n DS_SPINNER_SIZES,\n type DsSpinnerTone,\n type DsSpinnerSize,\n} from './DsSpinner.types';\n\n// Named size → px (DS STRING_SIZES). Kept local: only DsSpinner.vue needs the\n// resolved pixel value.\nconst SPINNER_SIZE_PX: Record<DsSpinnerSize, number> = {\n [DS_SPINNER_SIZES.SMALL]: 20,\n [DS_SPINNER_SIZES.MEDIUM]: 32,\n [DS_SPINNER_SIZES.LARGE]: 48,\n};\n\nconst props = defineProps({\n // Diameter in px, or a named size (\"small\" 20 / \"medium\" 32 / \"large\" 48).\n // Default (24) is unchanged and isn't one of the named steps.\n size: { type: [Number, String] as PropType<number | DsSpinnerSize>, default: 24 },\n tone: {\n type: String as PropType<DsSpinnerTone>,\n default: DS_SPINNER_TONES.DARK,\n },\n // Accessible name (DS default \"Loading\"); the consuming app can override for\n // i18n. role=\"status\" + aria-label are always present, never opt-in.\n label: { type: String, default: 'Loading' },\n});\n\nconst resolvedSize = computed(() =>\n typeof props.size === 'string' ? (SPINNER_SIZE_PX[props.size] ?? 24) : props.size\n);\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/motion.scss' as m;\n\n@keyframes ds-spinner-rot {\n to {\n transform: rotate(360deg);\n }\n}\n\n.ds-spinner {\n display: inline-block;\n\n border: 2px solid;\n border-radius: 50%;\n\n animation: ds-spinner-rot 1s linear infinite;\n\n @include m.reduce-motion {\n animation: none;\n }\n\n &--dark {\n border-color: colors.$slate-tint;\n border-top-color: colors.$slate-main;\n }\n &--light {\n border-color: rgba(255, 255, 255, 0.4);\n border-top-color: colors.$white;\n }\n &--accent {\n border-color: colors.$accent-mid;\n border-top-color: colors.$accent-main;\n }\n}\n</style>\n","// Generic click event for props/emit.\nexport const CLICK = 'click' as const;\nexport type ClickEventName = typeof CLICK;\n\n// Close event (e.g. DsTag with × / closable) — maps to the DS onClose.\nexport const CLOSE = 'close' as const;\nexport type CloseEventName = typeof CLOSE;\n\n// Focus events for form controls (DsInput / DsTextarea).\nexport const FOCUS = 'focus' as const;\nexport type FocusEventName = typeof FOCUS;\n\nexport const BLUR = 'blur' as const;\nexport type BlurEventName = typeof BLUR;\n\n// Value commit (DsInput): the native `change` event (blur/Enter with a changed\n// value), distinct from update:modelValue (the v-model's continuous input).\nexport const CHANGE = 'change' as const;\nexport type ChangeEventName = typeof CHANGE;\n\n// REAL field-exit focus signal (DsInput): focus left the whole component, not an\n// inner descendant. Unlike BLUR (the native `<input>` `blur`, which does NOT\n// bubble and therefore fires even when focus moves to the component's own\n// password toggle button), FIELD_BLUR uses `focusout` —which does bubble— plus\n// relatedTarget/`currentTarget.contains(next)` to emit only when focus leaves\n// outward. Payload: the FocusEvent.\nexport const FIELD_BLUR = 'field-blur' as const;\nexport type FieldBlurEventName = typeof FIELD_BLUR;\n\n// Data-family events (DsContactValue / DsDataCard).\n// COPY carries the copied value; CORRECT / INCORRECT mark a datum's rating.\nexport const COPY = 'copy' as const;\nexport type CopyEventName = typeof COPY;\n\nexport const CORRECT = 'correct' as const;\nexport type CorrectEventName = typeof CORRECT;\n\nexport const INCORRECT = 'incorrect' as const;\nexport type IncorrectEventName = typeof INCORRECT;\n\n// DsDataCard header actions.\nexport const SORT = 'sort' as const;\nexport type SortEventName = typeof SORT;\n\nexport const EDIT = 'edit' as const;\nexport type EditEventName = typeof EDIT;\n\n// DsTable bulk selection — the \"select all\" checkbox column.\n// Payload: the new boolean state.\nexport const SELECT_ALL = 'select-all' as const;\nexport type SelectAllEventName = typeof SELECT_ALL;\n\n// Pagination (DsPaginator). PAGE_CHANGE carries the new page number (1-based);\n// PAGE_SIZE_CHANGE carries the new page size.\nexport const PAGE_CHANGE = 'page-change' as const;\nexport type PageChangeEventName = typeof PAGE_CHANGE;\n\nexport const PAGE_SIZE_CHANGE = 'page-size-change' as const;\nexport type PageSizeChangeEventName = typeof PAGE_SIZE_CHANGE;\n\n// Text typed into DsSelect's embedded search box (prop `filterable`). Emitted on\n// every input change, and with '' when the dropdown closes and clears the filter\n// (so a `remote` consumer restores the full set on reopen; harmless locally). By\n// default DsSelect filters locally; an app wanting remote filtering uses `remote`\n// + listens to `search` to replace `options` (so local filtering isn't duplicated\n// on top of already-filtered results).\nexport const SEARCH = 'search' as const;\nexport type SearchEventName = typeof SEARCH;\n\n// Navigation-family events (DsMenuItem / DsWizardStepper).\n// SELECT: a menu row was chosen (carries the MouseEvent, like CLICK).\n// STEP: the wizard moved to a step (1-based number).\n// FINAL_STEP: the wizard's final action was triggered (no payload).\nexport const SELECT = 'select' as const;\nexport type SelectEventName = typeof SELECT;\n\nexport const STEP = 'step' as const;\nexport type StepEventName = typeof STEP;\n\nexport const FINAL_STEP = 'final-step' as const;\nexport type FinalStepEventName = typeof FINAL_STEP;\n\n// NAVIGATE: se clickeó el link de una notificación (DsNotification). Lleva el\n// MouseEvent (como SELECT), así el consumidor puede hacer preventDefault() y\n// enrutar in-app con el router de su app en vez de la navegación nativa del <a>.\nexport const NAVIGATE = 'navigate' as const;\nexport type NavigateEventName = typeof NAVIGATE;\n","// DsButton — 1:1 with the SalesQL Design System \"Button\" (components/core/Button.jsx).\n// Variants map to the DS `variantStyle`; sizes to the DS control heights.\n\n// Variants (classes: ds-button--<variant>)\nexport const DS_BUTTON_VARIANTS = {\n DEFAULT: 'default', // white surface, dark text (the default value; == tertiary)\n PRIMARY: 'primary', // solid accent fill, white text — the main CTA\n SECONDARY: 'secondary', // slate surface fill, dark text\n TERTIARY: 'tertiary', // white surface, dark text (low-emphasis row action)\n QUATERNARY: 'quaternary', // accent text + accent-mid border on white (former \"special\")\n QUINARY: 'quinary', // accent text on white, no border (link-style) (former \"special2\")\n SENARY: 'senary', // accent text on accent-tint; hover fills solid accent (former \"special3\")\n DOTTED: 'dotted', // transparent, dashed border, 24px radius — \"add\" affordance\n DANGER: 'danger', // destructive/delete — error text on error-tint (DS tint style)\n // Legacy: solid-red fill (white text) kept for back-compat. The DS treats\n // `warning` as a deprecated alias of `danger` (tint), but app consumers already\n // ship this solid look — kept unchanged to avoid a visual regression. Prefer `danger`.\n WARNING: 'warning',\n} as const;\nexport type DsButtonVariant = (typeof DS_BUTTON_VARIANTS)[keyof typeof DS_BUTTON_VARIANTS];\n\n// Sizes = DS control heights (classes: ds-button--<size>)\nexport const DS_BUTTON_SIZES = {\n SMALL: 'small', // 24px\n MEDIUM: 'medium', // 32px\n LARGE: 'large', // 36px\n XLARGE: 'xlarge', // 48px\n} as const;\nexport type DsButtonSize = (typeof DS_BUTTON_SIZES)[keyof typeof DS_BUTTON_SIZES];\n\n// Native <button> type\nexport const DS_BUTTON_NATIVE_TYPES = {\n BUTTON: 'button',\n SUBMIT: 'submit',\n RESET: 'reset',\n} as const;\nexport type DsButtonNativeType =\n (typeof DS_BUTTON_NATIVE_TYPES)[keyof typeof DS_BUTTON_NATIVE_TYPES];\n","<template>\n <button\n class=\"ds-button\"\n :type=\"nativeType\"\n :disabled=\"disabled || loading\"\n :autofocus=\"autofocus\"\n :aria-busy=\"loading || undefined\"\n :aria-disabled=\"disabled || loading || undefined\"\n :aria-pressed=\"selected || undefined\"\n :aria-label=\"ariaLabel || undefined\"\n :class=\"[\n `ds-button--${variant}`,\n `ds-button--${size}`,\n {\n 'is-disabled': disabled || loading,\n 'is-loading': loading,\n 'is-circle': circle,\n 'is-square': square,\n 'is-no-padding': noPadding,\n 'is-selected': selected,\n },\n ]\"\n @click=\"handleClick\"\n >\n <DsSpinner v-if=\"loading\" class=\"ds-button__spinner\" :size=\"iconSize\" :tone=\"spinnerTone\" />\n\n <!-- El contenido queda montado mientras loading (visibility-hidden + aria-hidden)\n para que el botón conserve su ancho: el spinner lo overlea, en vez de\n colapsar al ancho del spinner y volver a saltar al terminar. -->\n <span class=\"ds-button__content\" :aria-hidden=\"loading || undefined\">\n <span v-if=\"leftIcon || $slots.leading\" class=\"ds-button__icon ds-button__icon--leading\">\n <slot name=\"leading\"\n ><i class=\"material-icons\" aria-hidden=\"true\">{{ leftIcon }}</i></slot\n >\n </span>\n\n <span v-if=\"$slots.default && !circle && !square\" class=\"ds-button__label\"><slot /></span>\n\n <span v-if=\"rightIcon || $slots.trailing\" class=\"ds-button__icon ds-button__icon--trailing\">\n <slot name=\"trailing\"\n ><i class=\"material-icons\" aria-hidden=\"true\">{{ rightIcon }}</i></slot\n >\n </span>\n </span>\n </button>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, type PropType } from 'vue';\nimport { DsSpinner, DS_SPINNER_TONES } from '@shared/ui/DsSpinner';\nimport { CLICK } from '@shared/constants/events';\nimport {\n DS_BUTTON_NATIVE_TYPES,\n DS_BUTTON_SIZES,\n DS_BUTTON_VARIANTS,\n type DsButtonNativeType,\n type DsButtonSize,\n type DsButtonVariant,\n} from './DsButton.types';\n\nconst props = defineProps({\n variant: { type: String as PropType<DsButtonVariant>, default: DS_BUTTON_VARIANTS.DEFAULT },\n size: { type: String as PropType<DsButtonSize>, default: DS_BUTTON_SIZES.MEDIUM },\n nativeType: {\n type: String as PropType<DsButtonNativeType>,\n default: DS_BUTTON_NATIVE_TYPES.BUTTON,\n },\n // Material Icon names (rendered via <i class=\"material-icons\">); a leading/\n // trailing slot overrides. Icon-only buttons (circle/square) need ariaLabel.\n leftIcon: { type: String, default: '' },\n rightIcon: { type: String, default: '' },\n ariaLabel: { type: String, default: '' },\n disabled: Boolean,\n loading: Boolean,\n // Round icon-only button (width === height, fully circular).\n circle: Boolean,\n // Flat 4px-radius icon-only button (the DS `*-square` sizes): symmetric padding\n // per size, height === width.\n square: Boolean,\n // Zero the square padding for a tight, edge-to-edge glyph (DS `small-square-no-padding`).\n // Only takes effect together with `square`.\n noPadding: Boolean,\n // Toggle state: accent outline + fill; sets aria-pressed (DS `selected`).\n selected: Boolean,\n autofocus: Boolean,\n});\n\n// Runtime array (not the type-only form): the SFC compiler can't resolve\n// `typeof CLICK` to a literal, leaving `emits` empty at runtime. With `click`\n// undeclared, the parent's @click both falls through to the native <button>\n// AND is invoked by emit() → the listener fires twice. Declaring it at runtime\n// (via the CLICK constant, per convention) excludes it from fallthrough.\nconst emit = defineEmits([CLICK]);\n\nconst iconSize = computed(() => (props.size === DS_BUTTON_SIZES.SMALL ? 14 : 20));\n// Solid-fill variants (white text) take the light spinner; the rest, dark.\nconst spinnerTone = computed(() =>\n props.variant === DS_BUTTON_VARIANTS.PRIMARY || props.variant === DS_BUTTON_VARIANTS.WARNING\n ? DS_SPINNER_TONES.LIGHT\n : DS_SPINNER_TONES.DARK\n);\n\nfunction handleClick(event: MouseEvent) {\n if (props.disabled || props.loading) return;\n emit(CLICK, event);\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/spacing.scss' as s;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n@use '../../assets/css/icons.scss' as i;\n\n.ds-button {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n padding: 0;\n\n font-family: t.$font-family;\n font-weight: t.$weight-medium; // 500 (DS); small overrides to 400\n font-size: t.font-size('body');\n line-height: 20px;\n\n border: 1px solid transparent;\n border-radius: r.$radius-control;\n outline: none;\n\n cursor: pointer;\n transition:\n background-color m.$duration-default m.$easing,\n border-color m.$duration-default m.$easing,\n opacity m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n &.is-disabled {\n opacity: 0.55;\n cursor: not-allowed;\n }\n\n &__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n // DS icon size: 20px (small → 14px). Sizing the glyph to fill its box also\n // removes the optical \"gap\" a smaller glyph leaves against the button edge.\n .material-icons {\n font-size: i.icon-size(20);\n line-height: 1;\n }\n }\n\n // Material glyphs carry a built-in keyline (~2–3px advance bearing). Absorb it\n // on the edge side so a leading/trailing icon sits at the same optical inset as\n // a text-only button. Not for icon-only (circle/square) buttons — those stay centered.\n &:not(.is-circle):not(.is-square) &__icon--leading {\n margin-left: -3px;\n }\n &:not(.is-circle):not(.is-square) &__icon--trailing {\n margin-right: -3px;\n }\n\n // Content wrapper stays in flow while loading (hidden) so the button keeps its\n // width; the spinner is overlaid on top instead of replacing the content.\n &__content {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n }\n &.is-loading &__content {\n visibility: hidden;\n }\n\n // A button labels a single action — it must not wrap. With the height pinned per\n // size (control-height), wrappable text would break the control height; nowrap\n // makes it size to content and overflow horizontally (harmless) instead.\n &__label {\n white-space: nowrap;\n }\n\n // Overlaid over the (hidden) content — centered without a fixed size.\n // Size comes from the :size prop (iconSize).\n &__spinner {\n position: absolute;\n inset: 0;\n margin: auto;\n }\n\n /* ---------- Variants (DS variantStyle) ---------- */\n // `default` and `tertiary` are identical in the DS (white surface, dark text).\n &--default,\n &--tertiary {\n color: colors.$slate-main;\n background-color: colors.$white;\n\n &:hover:not(.is-disabled) {\n background-color: colors.$slate-tint;\n }\n }\n &--primary {\n color: colors.$white;\n background-color: colors.$accent-main;\n\n &:hover:not(.is-disabled) {\n background-color: colors.$accent-shadow;\n }\n }\n &--secondary {\n color: colors.$slate-main;\n background-color: colors.$slate-bg;\n\n &:hover:not(.is-disabled) {\n background-color: colors.$slate-tint;\n }\n }\n &--quaternary {\n color: colors.$accent-main;\n border-color: colors.$accent-mid;\n background-color: colors.$white;\n\n &:hover:not(.is-disabled) {\n border-color: colors.$accent-main;\n }\n }\n &--quinary {\n color: colors.$accent-main;\n background-color: colors.$white;\n\n &:hover:not(.is-disabled) {\n background-color: colors.$accent-bg;\n }\n }\n // Ordinal accent (tint fill) — accent text on accent-tint, weight 400; hover\n // fills solid accent with white text (DS `senary`, former \"special3\").\n &--senary {\n color: colors.$accent-main;\n font-weight: t.$weight-regular; // 400 (DS)\n background-color: colors.$accent-tint;\n\n &:hover:not(.is-disabled) {\n color: colors.$white;\n background-color: colors.$accent-main;\n }\n }\n // Destructive / delete (DS `danger`) — error text on error tint; hover deepens it.\n &--danger {\n color: colors.$error-main;\n background-color: colors.$error-tint;\n\n &:hover:not(.is-disabled) {\n background-color: colors.$error-mid;\n }\n }\n // Legacy solid-red fill (white text). Kept for back-compat; the DS aliases\n // `warning` → `danger` (tint), but consumers ship this solid look. Prefer `danger`.\n &--warning {\n color: colors.$white;\n background-color: colors.$error-main;\n\n &:hover:not(.is-disabled) {\n background-color: colors.$error-shadow;\n }\n }\n // \"Add\" affordance — dashed border, 24px radius, weight 700, no hover (DS).\n &--dotted {\n color: colors.$slate-mid;\n font-weight: t.$weight-bold;\n\n border: 1px dashed colors.$slate-mid;\n border-radius: r.$radius-dashed;\n background-color: transparent;\n }\n\n /* ---------- Selected (toggle) — wins over the variant, incl. hover ---------- */\n &.is-selected,\n &.is-selected:hover:not(.is-disabled) {\n color: colors.$accent-main;\n border-color: colors.$accent-main;\n background-color: colors.$accent-bg;\n }\n\n /* ---------- Sizes (DS control heights + paddings) ---------- */\n &--small {\n height: s.control-height('small');\n padding: 0 s.control-padding('small');\n\n font-weight: t.$weight-regular; // 400\n font-size: t.font-size('caption');\n\n .ds-button__content {\n gap: 2px;\n }\n .ds-button__icon .material-icons {\n font-size: i.icon-size(14);\n }\n }\n &--medium {\n height: s.control-height('medium');\n padding: 0 s.control-padding('medium');\n }\n &--large {\n height: s.control-height('large');\n padding: 0 s.control-padding('large');\n }\n &--xlarge {\n height: s.control-height('xlarge');\n padding: 0 s.control-padding('xlarge');\n }\n\n /* ---------- Circle (icon-only, true circle) ---------- */\n // width === height (= the size's control height): aspect-ratio alone lets the\n // content stretch the box into an oval, so pin the width to the height per size.\n &.is-circle {\n padding: 0;\n border-radius: r.$radius-full;\n }\n &--small.is-circle {\n width: s.control-height('small');\n }\n &--medium.is-circle {\n width: s.control-height('medium');\n }\n &--large.is-circle {\n width: s.control-height('large');\n }\n &--xlarge.is-circle {\n width: s.control-height('xlarge');\n }\n\n /* ---------- Square (icon-only, flat 4px) ---------- */\n // DS `*-square`: symmetric padding per size on a flat 4px radius, around a\n // centered glyph. Width is pinned to the control height per size (like circle):\n // with box-sizing:border-box the 1px border is baked into the fixed height but\n // NOT into the content-driven width, so padding alone leaves the box 2px wider\n // than tall. Pinning the width forces a true square.\n &.is-square {\n border-radius: r.$radius-control;\n }\n &--small.is-square {\n width: s.control-height('small');\n padding: s.control-square-padding('small');\n }\n &--medium.is-square {\n width: s.control-height('medium');\n padding: s.control-square-padding('medium');\n }\n &--large.is-square {\n width: s.control-height('large');\n padding: s.control-square-padding('large');\n }\n &--xlarge.is-square {\n width: s.control-height('xlarge');\n padding: s.control-square-padding('xlarge');\n }\n\n // Zero-padding square (DS `small-square-no-padding`): tight glyph in a fixed\n // square box. Like circle, pin the width to the size's control height so\n // dropping the padding can't collapse the width below the height — the button\n // stays square (height === width) per this component's invariant.\n &.is-square.is-no-padding {\n padding: 0;\n }\n &--small.is-square.is-no-padding {\n width: s.control-height('small');\n }\n &--medium.is-square.is-no-padding {\n width: s.control-height('medium');\n }\n &--large.is-square.is-no-padding {\n width: s.control-height('large');\n }\n &--xlarge.is-square.is-no-padding {\n width: s.control-height('xlarge');\n }\n}\n</style>\n","// DsTag — the DS \"Tag\": small bordered chip with optional close (×).\n// ⚠️ NOT the solid pill (that is DsBadge). See DESIGN-SYSTEM.md reconciliation.\n// Tones mirror the DS Tag source (Tag.jsx TONES). `rounded` → semicircular sides.\n\nexport const DS_TAG_TONES = {\n DEFAULT: 'default', // white bg, slate-tint border, muted text\n GREY: 'grey', // alias of `default` — the DS app passes tone=\"grey\" (ApiApp.jsx)\n SUCCESS: 'success', // e.g. Verified\n INFO: 'info', // e.g. Work\n WARNING: 'warning', // e.g. Pending\n ERROR: 'error', // e.g. Bounced\n} as const;\nexport type DsTagTone = (typeof DS_TAG_TONES)[keyof typeof DS_TAG_TONES];\n","<template>\n <span\n class=\"ds-tag\"\n :class=\"[\n `ds-tag--${tone}`,\n { 'is-rounded': rounded, 'is-clickable': clickable, 'is-truncate': truncate },\n ]\"\n >\n <!-- Label wrapper: only this clips under is-truncate. The close button is a\n sibling (outside this span), so it is never affected by the ellipsis. -->\n <span class=\"ds-tag__label\"><slot /></span>\n\n <button\n v-if=\"closable\"\n type=\"button\"\n class=\"ds-tag__close\"\n :aria-label=\"closeLabel || undefined\"\n @click.stop=\"emit(CLOSE)\"\n >\n <i class=\"material-icons\" aria-hidden=\"true\">close</i>\n </button>\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport { type PropType } from 'vue';\nimport { CLOSE } from '@shared/constants/events';\nimport { DS_TAG_TONES, type DsTagTone } from './DsTag.types';\n\ndefineProps({\n tone: {\n type: String as PropType<DsTagTone>,\n default: DS_TAG_TONES.DEFAULT,\n },\n // Semicircular sides (DS `rounded`): radius-round vs the default radius-control.\n rounded: Boolean,\n // Show a × close control that emits `close`.\n closable: Boolean,\n // Clickable affordance (DS: presence of `onClick` toggles cursor: pointer).\n // Vue can't cheaply detect an attached native @click listener, so this is an\n // explicit opt-in; it only sets cursor styling and doesn't touch the native\n // @click fallthrough behavior.\n clickable: Boolean,\n // Opt-in single-line truncation for long labels (default off). The consuming\n // app sets the ceiling via the `--ds-tag-max-width` CSS custom property; the\n // label then clips with an ellipsis while the close control stays intact.\n truncate: Boolean,\n // Accessible name for the × control (the app owns the wording / i18n).\n closeLabel: {\n type: String,\n default: '',\n },\n});\n\nconst emit = defineEmits<{\n (e: typeof CLOSE): void;\n}>();\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/icons.scss' as i;\n\n.ds-tag {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n\n padding: 0 10px;\n\n color: colors.$slate-mid;\n font-family: t.$font-family;\n font-weight: t.$weight-regular;\n font-size: t.font-size('caption');\n line-height: 30px;\n\n background-color: colors.$white;\n\n border: 1px solid colors.$slate-tint;\n border-radius: r.$radius-control;\n\n white-space: nowrap;\n\n // Allow the label to shrink below its content width so the ellipsis can kick\n // in when the chip is capped (no visible effect until is-truncate + a cap).\n min-width: 0;\n\n &.is-rounded {\n border-radius: r.$radius-round;\n }\n\n // Clickable affordance (DS: cursor: pointer when an onClick handler is present).\n &.is-clickable {\n cursor: pointer;\n }\n\n // Opt-in truncation: the app sets the ceiling via `--ds-tag-max-width` (a CSS\n // custom property, not a hardcoded px), defaulting to `none` (uncapped).\n &.is-truncate {\n max-width: var(--ds-tag-max-width, none);\n }\n\n &__label {\n min-width: 0;\n }\n\n // Only the label clips; the close button (a sibling) is untouched.\n &.is-truncate &__label {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n\n &__close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n // Never shrink or clip the close control, however tight the cap.\n flex-shrink: 0;\n\n padding: 0;\n\n color: inherit;\n\n background: transparent;\n border: 0;\n\n cursor: pointer;\n\n .material-icons {\n font-size: i.icon-size(12);\n }\n }\n\n /* ---------- Tones (default: white + tint border + muted text) ---------- */\n // `grey` is a DS alias of `default` (the DS app passes tone=\"grey\").\n &--default,\n &--grey {\n color: colors.$slate-mid;\n border-color: colors.$slate-tint;\n background-color: colors.$white;\n }\n &--success {\n color: colors.$success-shadow;\n border-color: colors.$success-mid;\n background-color: colors.$success-tint;\n }\n &--info {\n color: colors.$info-shadow;\n border-color: colors.$info-mid;\n background-color: colors.$info-tint;\n }\n &--warning {\n color: colors.$warning-shadow;\n border-color: colors.$warning-mid;\n background-color: colors.$warning-tint;\n }\n &--error {\n color: colors.$error-shadow;\n border-color: colors.$error-mid;\n background-color: colors.$error-tint;\n }\n}\n</style>\n","// DsBadge — the DS \"Badge\": solid, white-text pill (value types & counts).\n// ⚠️ NOT the tint chip (that is DsTag). See DESIGN-SYSTEM.md reconciliation.\n// Tones mirror the DS Badge source (Badge.jsx TONES).\n\nexport const DS_BADGE_TONES = {\n ACCENT: 'accent', // e.g. Work (purple)\n GREY: 'grey', // e.g. Direct (slate-soft)\n INFO: 'info', // e.g. Mobile (blue)\n SUCCESS: 'success', // e.g. New (green)\n ERROR: 'error', // coral\n WARNING: 'warning', // orange\n} as const;\nexport type DsBadgeTone = (typeof DS_BADGE_TONES)[keyof typeof DS_BADGE_TONES];\n","<template>\n <span class=\"ds-badge\" :class=\"`ds-badge--${tone}`\">\n <slot />\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport { type PropType } from 'vue';\nimport { DS_BADGE_TONES, type DsBadgeTone } from './DsBadge.types';\n\ndefineProps({\n tone: {\n type: String as PropType<DsBadgeTone>,\n default: DS_BADGE_TONES.ACCENT,\n },\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n\n.ds-badge {\n display: inline-flex;\n align-items: center;\n\n height: 17px;\n padding: 0 8px;\n\n color: colors.$white;\n font-family: t.$font-family;\n font-weight: t.$weight-bold;\n font-size: t.font-size('caption');\n line-height: 17px;\n\n border-radius: r.$radius-pill;\n\n white-space: nowrap;\n\n /* ---------- Tones (solid fill, white text) ---------- */\n &--accent {\n background-color: colors.$accent-main;\n }\n &--grey {\n background-color: colors.$slate-soft;\n }\n &--info {\n background-color: colors.$info-main;\n }\n &--success {\n background-color: colors.$success-main;\n }\n &--error {\n background-color: colors.$error-main;\n }\n &--warning {\n background-color: colors.$warning-main;\n }\n}\n</style>\n","<template>\n <img\n v-if=\"image\"\n class=\"ds-avatar\"\n :class=\"{ 'is-circular': circular }\"\n :src=\"image\"\n :alt=\"ariaLabel\"\n :style=\"boxStyle\"\n />\n\n <div\n v-else-if=\"initials\"\n class=\"ds-avatar ds-avatar--initials\"\n :class=\"{ 'is-circular': circular }\"\n role=\"img\"\n :aria-label=\"ariaLabel || undefined\"\n :style=\"[\n boxStyle,\n {\n background: hashed.bg,\n color: hashed.fg,\n fontSize: `${resolvedTextSize}px`,\n fontWeight: fontWeight,\n },\n ]\"\n >\n {{ initials }}\n </div>\n\n <div\n v-else\n class=\"ds-avatar ds-avatar--placeholder\"\n :class=\"{ 'is-circular': circular }\"\n :style=\"boxStyle\"\n aria-hidden=\"true\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue';\n\nconst props = defineProps({\n image: { type: String, default: '' },\n firstName: { type: String, default: '' },\n lastName: { type: String, default: '' },\n // Explicit initials (DS `letters`) — overrides the computed firstName+lastName\n // initials when non-empty. Passed through as-is (no uppercase transform),\n // matching the DS: only the firstName+lastName path uppercases.\n letters: { type: String, default: '' },\n uid: { type: String, default: '' },\n size: { type: Number, default: 40 },\n // Explicit width/height (DS parity) — override `size` per-axis when present.\n width: { type: Number, default: undefined },\n height: { type: Number, default: undefined },\n // Initials font-size in px — overrides the computed ~40%-of-box value when present.\n textSize: { type: Number, default: undefined },\n // Initials font-weight — overrides the default token weight (900) when present.\n fontWeight: { type: [Number, String], default: undefined },\n circular: Boolean,\n // Accessible label. Omitted → falls back to the computed name/letters. An explicit\n // value wins — including `alt=\"\"` for a deliberately decorative image (default is\n // `undefined`, so \"\" is distinguishable from \"not provided\").\n alt: { type: String, default: undefined },\n});\n\nconst initials = computed(() => {\n if (props.letters) return props.letters;\n return props.firstName && props.lastName\n ? (props.firstName.charAt(0) + props.lastName.charAt(0)).toUpperCase()\n : '';\n});\n\nconst derivedLabel = computed(() => `${props.firstName} ${props.lastName}`.trim() || props.letters);\n// `??` (not `||`): an explicit \"\" (decorative) is preserved; only a truly absent\n// `alt` (undefined) falls back to the derived name/letters.\nconst ariaLabel = computed(() => props.alt ?? derivedLabel.value);\n\nconst boxWidth = computed(() => props.width ?? props.size);\nconst boxHeight = computed(() => props.height ?? props.size);\nconst boxStyle = computed(() => ({ width: `${boxWidth.value}px`, height: `${boxHeight.value}px` }));\n\nconst resolvedTextSize = computed(\n () => props.textSize ?? Math.round(Math.min(boxWidth.value, boxHeight.value) * 0.4)\n);\n\n// Deterministic color: seed on uid, else the name, else the explicit `letters`\n// (the DS seeds on `uid || initials`, so a letters-only avatar must feed its\n// letters into the hash — otherwise every letters-only avatar hashes the empty\n// string and collapses to the same color). `letters` last keeps existing\n// name-based avatars on their current color (additive, no visual regression).\nconst hashed = computed(() => {\n const seed = props.uid || props.firstName + props.lastName || props.letters;\n let hash = 0;\n for (let i = 0; i < seed.length; i++) {\n hash = seed.charCodeAt(i) + ((hash << 5) - hash);\n hash = hash & hash;\n }\n const rgb = [0, 0, 0];\n for (let i = 0; i < 3; i++) rgb[i] = (hash >> (i * 8)) & 255;\n const light = rgb[0] >= 200 && rgb[1] >= 200 && rgb[2] >= 200;\n return { bg: `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`, fg: light ? '#323f4b' : '#fff' };\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n\n.ds-avatar {\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n\n overflow: hidden;\n object-fit: cover;\n\n border-radius: r.$radius-control;\n\n &.is-circular {\n border-radius: r.$radius-full;\n }\n\n &--initials {\n font-family: t.$font-family;\n font-weight: t.$weight-black;\n line-height: 1;\n user-select: none;\n }\n\n &--placeholder {\n // DS placeholder grey — was hardcoded #c0c4cc; the real DS token is\n // --grey-3 (#cbd2d9). Fixed to use the token (SALESQL-3394).\n background-color: colors.$grey-3;\n }\n}\n</style>\n","<template>\n <label class=\"ds-checkbox\" :class=\"{ 'is-disabled': disabled }\">\n <input\n ref=\"inputRef\"\n v-model=\"model\"\n type=\"checkbox\"\n class=\"ds-checkbox__native\"\n :disabled=\"disabled\"\n :name=\"name || undefined\"\n :aria-label=\"ariaLabel || undefined\"\n />\n <span\n class=\"ds-checkbox__box\"\n :class=\"{ 'is-active': model || indeterminate }\"\n aria-hidden=\"true\"\n >\n <svg\n v-if=\"indeterminate\"\n class=\"ds-checkbox__mark\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n >\n <line x1=\"4\" y1=\"8\" x2=\"12\" y2=\"8\" />\n </svg>\n <svg\n v-else-if=\"model\"\n class=\"ds-checkbox__mark\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n >\n <path d=\"M3.5 8.5l3 3 6-6\" />\n </svg>\n </span>\n <span v-if=\"$slots.default || label\" class=\"ds-checkbox__label\"\n ><slot>{{ label }}</slot></span\n >\n </label>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onMounted, watch } from 'vue';\n\nconst model = defineModel<boolean>({ default: false });\n\nconst props = defineProps({\n indeterminate: Boolean,\n disabled: Boolean,\n name: { type: String, default: '' },\n // Label text (DS `label` prop); a default slot overrides it.\n label: { type: String, default: '' },\n // Accessible name when there is no visible label (e.g. a select-all in a table).\n ariaLabel: { type: String, default: '' },\n});\n\nconst inputRef = ref<HTMLInputElement | null>(null);\n\n// `indeterminate` is a DOM property, not an attribute — keep it in sync for a11y.\nfunction syncIndeterminate() {\n if (inputRef.value) inputRef.value.indeterminate = props.indeterminate;\n}\nonMounted(syncIndeterminate);\nwatch(() => props.indeterminate, syncIndeterminate);\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/spacing.scss' as s;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n\n.ds-checkbox {\n display: inline-flex;\n align-items: center;\n gap: s.spacing(1);\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n\n cursor: pointer;\n\n &.is-disabled {\n cursor: not-allowed;\n opacity: 0.5; // DS Checkbox.jsx: whole label dims to .5 when disabled\n }\n\n /* Native control drives state + focus, visually replaced by the custom box. */\n &__native {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n }\n\n &__box {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n\n width: 18px;\n height: 18px;\n\n color: colors.$white;\n\n background-color: colors.$white;\n\n border: 1px solid colors.$slate-mid;\n border-radius: r.$radius-control;\n\n transition:\n background-color m.$duration-default m.$easing,\n border-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n &.is-active {\n background-color: colors.$accent-main;\n border-color: colors.$accent-main;\n }\n }\n\n /* Focus ring on the box when the (hidden) native input is focused. */\n &__native:focus-visible + &__box {\n outline: 2px solid colors.$accent-mid;\n outline-offset: 1px;\n }\n\n &__mark {\n width: 14px;\n height: 14px;\n }\n\n &__label {\n line-height: 1.3;\n }\n}\n</style>\n","// DsTable — the DS data table (data-dense density). Column/row API with hairline\n// borders, row hover, and an optional highlighted \"most likely\" row.\n\nexport const DS_TABLE_ALIGN = {\n LEFT: 'left',\n CENTER: 'center',\n RIGHT: 'right',\n} as const;\nexport type DsTableAlign = (typeof DS_TABLE_ALIGN)[keyof typeof DS_TABLE_ALIGN];\n\n// Which edge a column is pinned (sticky) to for horizontal scroll.\nexport const DS_TABLE_PIN = {\n LEFT: 'left',\n RIGHT: 'right',\n} as const;\nexport type DsTablePin = (typeof DS_TABLE_PIN)[keyof typeof DS_TABLE_PIN];\n\n// A column. `key` maps to the row property for default content and names the\n// scoped slot `cell-<key>`.\nexport interface DsTableColumn {\n key: string;\n label: string;\n align?: DsTableAlign;\n width?: string;\n // Pin this column to the left/right edge (position: sticky) so it stays put\n // while the table scrolls horizontally. Opt-in; the default layout is unchanged.\n // Stacking MORE THAN ONE pin on the same side needs a px `width` on each pinned\n // column so cumulative offsets can be derived; a single pin per side needs none.\n pinned?: DsTablePin;\n}\n\n// A header cell for the compound shell. `headers` accepts plain strings (still\n// valid) or this object form when a header needs to be pinned. Mirror the pinned\n// header's `width` on its matching DsCell so the sticky columns line up.\nexport interface DsTableHeader {\n label: string;\n pinned?: DsTablePin;\n width?: string;\n}\n\n// A row: only a stable `id` is required (for :key and highlighting); the rest of\n// the properties are consumed per column via its `key`.\nexport interface DsTableRow {\n id: string | number;\n [key: string]: unknown;\n}\n","<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, ref, watch, type PropType } from 'vue';\nimport { DsCheckbox } from '@shared/ui/DsCheckbox';\nimport { DsSpinner } from '@shared/ui/DsSpinner';\nimport { SELECT_ALL } from '@shared/constants/events';\nimport {\n DS_TABLE_ALIGN,\n DS_TABLE_PIN,\n type DsTableColumn,\n type DsTableHeader,\n type DsTableRow,\n} from './DsTable.types';\n\nconst props = defineProps({\n // --- Declarative API (default): the table builds itself from columns + rows. ---\n columns: {\n type: Array as PropType<DsTableColumn[]>,\n default: () => [],\n },\n rows: {\n type: Array as PropType<DsTableRow[]>,\n default: () => [],\n },\n emptyMessage: {\n type: String,\n default: '',\n },\n // id of the row to highlight (the DS \"most likely\" row → accent surface).\n highlightRowId: {\n type: [String, Number] as PropType<string | number | null>,\n default: null,\n },\n // Optional fixed row height (number → px, or any CSS length). Rows are\n // content-driven by default; when set, this pins the height AND truncates\n // overflowing cell text with an ellipsis.\n rowHeight: {\n type: [String, Number] as PropType<string | number | null>,\n default: null,\n },\n\n // --- Compound API (opt-in): compose DsTableRow + DsTableCell in the default slot. ---\n // When true, the table renders a shell (headers + a sticky thead) and the body\n // comes from the default slot instead of `rows`. Coexists with the declarative API.\n compound: { type: Boolean, default: false },\n // Header labels for the compound shell. Plain strings, or the DsTableHeader\n // object form when a header column needs to be pinned (mirror its `width` on\n // the matching DsCell so the sticky columns line up).\n headers: { type: Array as PropType<(string | DsTableHeader)[]>, default: () => [] },\n // Render a leading select-all checkbox column (compound mode).\n selectable: { type: Boolean, default: false },\n allSelected: { type: Boolean, default: false },\n someSelected: { type: Boolean, default: false },\n // Accessible name for the select-all checkbox (no visible text in a 52px column).\n selectAllLabel: { type: String, default: '' },\n // Fixed height enabling the sticky-header scroll (compound mode).\n height: {\n type: [String, Number] as PropType<string | number | null>,\n default: null,\n },\n\n // --- Loading state (both APIs) ---\n // When true, the body is a single centered spinner row spanning every column,\n // shown INSTEAD of the empty state or the rows/slot (loading wins over both).\n loading: { type: Boolean, default: false },\n // Accessible name for the loading spinner (the app owns i18n); when omitted it\n // falls back to DsSpinner's own default. Overridable per row via the #loading slot.\n loadingLabel: { type: String, default: '' },\n});\n\nconst emit = defineEmits([SELECT_ALL]);\n\n// Column span for the full-width loading row in compound mode: the header\n// columns plus the leading select-all column when selectable. Floor at 1 so an\n// empty `headers` array never emits an invalid colspan=\"0\".\nconst compoundColspan = computed(() =>\n Math.max(1, props.headers.length + (props.selectable ? 1 : 0))\n);\n\nconst rowHeightStyle = computed(() =>\n props.rowHeight == null\n ? undefined\n : {\n '--ds-table-row-height':\n typeof props.rowHeight === 'number' ? `${props.rowHeight}px` : props.rowHeight,\n }\n);\n\nconst heightStyle = computed(() =>\n props.height == null\n ? undefined\n : { height: typeof props.height === 'number' ? `${props.height}px` : props.height }\n);\n\nfunction onSelectAll(checked: boolean) {\n emit(SELECT_ALL, checked);\n}\n\n// --- Pinned (sticky) columns ---------------------------------------------\n// Only a px width contributes to a cumulative pin offset; %/auto/undefined → 0\n// (a single pin per side sits at offset 0 and needs no width).\nfunction pinWidthPx(width?: string): number {\n if (!width) return 0;\n const match = /^(\\d+(?:\\.\\d+)?)px$/.exec(width.trim());\n return match ? parseFloat(match[1]) : 0;\n}\n\ntype PinStyle = { position: 'sticky'; left?: string; right?: string };\n\n// Declarative API: per-column-key sticky side + cumulative offset (left grows\n// left→right; right grows right→left).\nconst columnPinStyles = computed(() => {\n const map: Record<string, PinStyle> = {};\n let left = 0;\n for (const col of props.columns) {\n if (col.pinned === DS_TABLE_PIN.LEFT) {\n map[col.key] = { position: 'sticky', left: `${left}px` };\n left += pinWidthPx(col.width);\n }\n }\n let right = 0;\n for (let i = props.columns.length - 1; i >= 0; i--) {\n const col = props.columns[i];\n if (col.pinned === DS_TABLE_PIN.RIGHT) {\n map[col.key] = { position: 'sticky', right: `${right}px` };\n right += pinWidthPx(col.width);\n }\n }\n return map;\n});\n\n// Outermost pin on each side — the only cell that carries the edge shadow.\nconst pinEdgeKeys = computed(() => {\n const lefts = props.columns.filter((c) => c.pinned === DS_TABLE_PIN.LEFT);\n const rights = props.columns.filter((c) => c.pinned === DS_TABLE_PIN.RIGHT);\n const set = new Set<string>();\n if (lefts.length) set.add(lefts[lefts.length - 1].key);\n if (rights.length) set.add(rights[0].key);\n return set;\n});\n\nfunction columnPinClass(column: DsTableColumn) {\n if (!column.pinned) return undefined;\n return {\n 'ds-table__pin': true,\n [`ds-table__pin--${column.pinned}`]: true,\n 'ds-table__pin--edge': pinEdgeKeys.value.has(column.key),\n };\n}\n\n// th keeps its width always (existing behaviour) merged with the sticky offset.\nfunction columnHeaderStyle(column: DsTableColumn) {\n const pin = columnPinStyles.value[column.key];\n const width = column.width ? { width: column.width } : undefined;\n if (!pin && !width) return undefined;\n return { ...width, ...pin };\n}\n\n// td only gets a style when pinned (non-pinned cells stay unstyled as before);\n// a pinned cell also takes the width so it lines up with its header.\nfunction columnCellStyle(column: DsTableColumn) {\n const pin = columnPinStyles.value[column.key];\n if (!pin) return undefined;\n return column.width ? { ...pin, width: column.width } : pin;\n}\n\n// Compound API: normalize the header union to objects; DsTable owns every header\n// so it computes header offsets centrally (body-cell offsets are the consumer's\n// via DsCell `pinnedOffset`).\nconst normalizedHeaders = computed<DsTableHeader[]>(() =>\n props.headers.map((h) => (typeof h === 'string' ? { label: h } : h))\n);\n\nconst headerPinStyles = computed(() => {\n const map: Record<number, PinStyle> = {};\n const list = normalizedHeaders.value;\n let left = 0;\n list.forEach((h, i) => {\n if (h.pinned === DS_TABLE_PIN.LEFT) {\n map[i] = { position: 'sticky', left: `${left}px` };\n left += pinWidthPx(h.width);\n }\n });\n let right = 0;\n for (let i = list.length - 1; i >= 0; i--) {\n if (list[i].pinned === DS_TABLE_PIN.RIGHT) {\n map[i] = { position: 'sticky', right: `${right}px` };\n right += pinWidthPx(list[i].width);\n }\n }\n return map;\n});\n\nconst headerPinEdges = computed(() => {\n const list = normalizedHeaders.value;\n const lefts = list.map((h, i) => ({ h, i })).filter(({ h }) => h.pinned === DS_TABLE_PIN.LEFT);\n const rights = list.map((h, i) => ({ h, i })).filter(({ h }) => h.pinned === DS_TABLE_PIN.RIGHT);\n const set = new Set<number>();\n if (lefts.length) set.add(lefts[lefts.length - 1].i);\n if (rights.length) set.add(rights[0].i);\n return set;\n});\n\nfunction headerPinClass(header: DsTableHeader, index: number) {\n if (!header.pinned) return undefined;\n return {\n 'ds-table__pin': true,\n [`ds-table__pin--${header.pinned}`]: true,\n 'ds-table__pin--edge': headerPinEdges.value.has(index),\n };\n}\n\nfunction headerStyle(header: DsTableHeader, index: number) {\n const pin = headerPinStyles.value[index];\n const width = header.width ? { width: header.width } : undefined;\n if (!pin && !width) return undefined;\n return { ...width, ...pin };\n}\n\n// --- Horizontal scroll shadow --------------------------------------------\n// The edge pins cast a directional shadow, revealed only while there is content\n// scrolled under them. We toggle two flags from the wrapper's scroll geometry.\nconst wrapperRef = ref<HTMLElement | null>(null);\nconst scrolledStart = ref(false); // content hidden to the left of a left pin\nconst scrolledEnd = ref(false); // content hidden to the right of a right pin\nlet resizeObserver: ResizeObserver | null = null;\n\nfunction updateScrollShadow() {\n const el = wrapperRef.value;\n if (!el) return;\n scrolledStart.value = el.scrollLeft > 0;\n scrolledEnd.value = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;\n}\n\nfunction teardownScroll(el: HTMLElement | null) {\n el?.removeEventListener('scroll', updateScrollShadow);\n resizeObserver?.disconnect();\n resizeObserver = null;\n}\n\n// React to the wrapper element mounting (or swapping when `compound` toggles).\n// `immediate` runs during setup with a null ref (no-op) and again once mounted.\n// ResizeObserver is guarded — jsdom (unit tests) doesn't implement it.\nwatch(\n wrapperRef,\n (el, prev) => {\n teardownScroll(prev ?? null);\n if (!el) return;\n el.addEventListener('scroll', updateScrollShadow, { passive: true });\n if (typeof ResizeObserver !== 'undefined') {\n resizeObserver = new ResizeObserver(updateScrollShadow);\n resizeObserver.observe(el);\n }\n updateScrollShadow();\n },\n { immediate: true }\n);\n\nonBeforeUnmount(() => teardownScroll(wrapperRef.value));\n\n// Structural changes can change scrollWidth → re-evaluate after the DOM updates.\n// Watch cheap length signals, not the arrays deeply: content-driven reflow (a\n// cell value changing width without a count change) is already caught by the\n// ResizeObserver on the wrapper, and a deep watch over `rows` would be costly on\n// the large tables pinned columns target.\nwatch(\n () => [props.columns.length, props.rows.length, props.headers.length, props.compound],\n () => nextTick(updateScrollShadow)\n);\n</script>\n\n<template>\n <!-- Compound (opt-in): the consumer composes DsTableRow / DsTableCell in the slot. -->\n <div\n v-if=\"compound\"\n class=\"ds-table ds-table--compound\"\n :aria-busy=\"loading ? 'true' : undefined\"\n >\n <div\n ref=\"wrapperRef\"\n class=\"ds-table__wrapper\"\n :class=\"{ 'is-scrolled-start': scrolledStart, 'is-scrolled-end': scrolledEnd }\"\n :style=\"heightStyle\"\n >\n <table class=\"ds-table__table\">\n <thead class=\"ds-table__head\">\n <tr>\n <th v-if=\"selectable\" class=\"ds-table__select-all\">\n <DsCheckbox\n :model-value=\"allSelected\"\n :indeterminate=\"!allSelected && someSelected\"\n :aria-label=\"selectAllLabel || undefined\"\n @update:model-value=\"onSelectAll\"\n />\n </th>\n <th\n v-for=\"(header, i) in normalizedHeaders\"\n :key=\"header.label\"\n :class=\"headerPinClass(header, i)\"\n :style=\"headerStyle(header, i)\"\n >\n {{ header.label }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr v-if=\"loading\">\n <td class=\"ds-table__loading\" :colspan=\"compoundColspan\" data-testid=\"ds-table-loading\">\n <slot name=\"loading\"><DsSpinner :label=\"loadingLabel || undefined\" /></slot>\n </td>\n </tr>\n <slot v-else />\n </tbody>\n </table>\n </div>\n </div>\n\n <!-- Declarative (default): columns + rows drive the whole table. -->\n <div v-else class=\"ds-table\" :aria-busy=\"loading ? 'true' : undefined\">\n <div\n ref=\"wrapperRef\"\n class=\"ds-table__wrapper\"\n :class=\"{ 'is-scrolled-start': scrolledStart, 'is-scrolled-end': scrolledEnd }\"\n >\n <table\n class=\"ds-table__table\"\n :class=\"{ 'has-row-height': rowHeight != null }\"\n :style=\"rowHeightStyle\"\n >\n <thead>\n <tr>\n <th\n v-for=\"column in columns\"\n :key=\"column.key\"\n :class=\"[\n `ds-table__th--${column.align ?? DS_TABLE_ALIGN.LEFT}`,\n columnPinClass(column),\n ]\"\n :style=\"columnHeaderStyle(column)\"\n >\n {{ column.label }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr v-if=\"loading\">\n <td\n class=\"ds-table__loading\"\n :colspan=\"Math.max(1, columns.length)\"\n data-testid=\"ds-table-loading\"\n >\n <slot name=\"loading\"><DsSpinner :label=\"loadingLabel || undefined\" /></slot>\n </td>\n </tr>\n <tr v-else-if=\"rows.length === 0\">\n <td\n class=\"ds-table__empty\"\n :colspan=\"Math.max(1, columns.length)\"\n data-testid=\"ds-table-empty\"\n >\n <slot name=\"empty\">{{ emptyMessage }}</slot>\n </td>\n </tr>\n <template v-else>\n <tr\n v-for=\"row in rows\"\n :key=\"row.id\"\n :class=\"{ 'is-highlighted': row.id === highlightRowId }\"\n data-testid=\"ds-table-row\"\n >\n <td\n v-for=\"column in columns\"\n :key=\"column.key\"\n :class=\"[\n `ds-table__td--${column.align ?? DS_TABLE_ALIGN.LEFT}`,\n columnPinClass(column),\n ]\"\n :style=\"columnCellStyle(column)\"\n :title=\"\n rowHeight != null && !$slots[`cell-${column.key}`] && row[column.key] != null\n ? String(row[column.key])\n : undefined\n \"\n >\n <slot :name=\"`cell-${column.key}`\" :row=\"row\">{{ row[column.key] }}</slot>\n </td>\n </tr>\n </template>\n </tbody>\n </table>\n </div>\n </div>\n</template>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/spacing.scss' as s;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n@use '../../assets/css/shadow.scss' as sh;\n\n.ds-table {\n &__wrapper {\n overflow-x: auto;\n\n border: 1px solid colors.$slate-tint;\n border-radius: r.$radius-card-lg;\n }\n\n &__table {\n width: 100%;\n border-collapse: collapse;\n\n // Own every border/height explicitly so host-app global table rules can't\n // leak in; scoped specificity (block + [data-v]) beats them.\n th {\n padding: s.spacing(1) s.spacing(2);\n\n color: colors.$slate-main;\n font-weight: t.$weight-bold;\n font-size: t.font-size('body');\n\n border: 0;\n border-bottom: 1px solid colors.$slate-tint;\n\n background-color: colors.$slate-bg;\n }\n\n td {\n height: auto;\n padding: s.spacing(1) s.spacing(2);\n vertical-align: middle;\n\n color: colors.$slate-main;\n font-size: t.font-size('body');\n\n border: 0;\n }\n\n // Opt-in fixed row height → pin td height and truncate overflowing text.\n &.has-row-height td {\n height: var(--ds-table-row-height);\n\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n\n tr {\n border: 0;\n transition: background-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n }\n\n // Row separator only BETWEEN adjacent body rows.\n tbody tr + tr {\n border-top: 1px solid colors.$slate-tint;\n }\n\n // Row hover (data-dense affordance).\n tbody tr:hover {\n background-color: colors.$slate-bg;\n }\n\n // The highlighted \"most likely\" row wins over hover.\n tbody tr.is-highlighted {\n background-color: colors.$accent-bg;\n }\n tbody tr.is-highlighted:hover {\n background-color: colors.$accent-tint;\n }\n }\n\n &__th--left,\n &__td--left {\n text-align: left;\n }\n &__th--center,\n &__td--center {\n text-align: center;\n }\n &__th--right,\n &__td--right {\n text-align: right;\n }\n\n // Full-width empty / loading rows. `.ds-table__table td` above is (0,1,1); a\n // bare `.ds-table__empty` / `.ds-table__loading` is only (0,1,0) and loses to\n // it, so the shared td `padding` (and `color`) would win — collapsing the row\n // to 8px and clamping the spinner to the top. Qualify with `td` + the table\n // context (→ 0,2,1) so the generous padding and centering actually apply.\n &__table td#{&}__empty {\n padding: s.spacing(5) s.spacing(3);\n\n color: colors.$slate-soft;\n text-align: center;\n }\n\n // Loading row: same generous padding as the empty state; the inline-block\n // spinner is centered horizontally by text-align and vertically by the td's\n // vertical-align:middle (works for both APIs).\n &__table td#{&}__loading {\n padding: s.spacing(5) s.spacing(3);\n text-align: center;\n }\n\n /* ---------- Pinned (sticky) columns ---------- */\n // Shared by both APIs (declarative th/td; compound header th — compound body\n // cells are DsCell, styled in DsCell.vue). position / left / right + width\n // arrive via the inline style; here we own the opaque background (so scrolling\n // cells never bleed through), the stacking order, and the edge shadow.\n &__pin {\n background-color: colors.$white;\n }\n thead &__pin {\n // Header pins sit on the grey header surface, above sibling header cells.\n z-index: 3;\n background-color: colors.$slate-bg;\n }\n tbody &__pin {\n // Body pins above normal body cells, below the compound sticky header (10).\n z-index: 2;\n }\n // Body pin tracks the declarative row background states (white / hover / accent).\n tbody tr:hover &__pin {\n background-color: colors.$slate-bg;\n }\n tbody tr.is-highlighted &__pin {\n background-color: colors.$accent-bg;\n }\n tbody tr.is-highlighted:hover &__pin {\n background-color: colors.$accent-tint;\n }\n // The outermost pin on each side casts a directional shadow, revealed only when\n // there is content scrolled under it (wrapper toggles the scrolled-* classes).\n &__pin--edge {\n transition: box-shadow m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n }\n &__wrapper.is-scrolled-start &__pin--left#{&}__pin--edge {\n box-shadow: sh.$shadow-pinned-left;\n }\n &__wrapper.is-scrolled-end &__pin--right#{&}__pin--edge {\n box-shadow: sh.$shadow-pinned-right;\n }\n\n /* ---------- Compound shell (opt-in) ---------- */\n &--compound {\n .ds-table__wrapper {\n overflow: auto; // both axes; height (prop) enables the sticky-header scroll\n }\n\n .ds-table__table {\n // Fixed layout + separate borders so the sticky header keeps its hairlines.\n border-collapse: separate;\n border-spacing: 0;\n table-layout: fixed;\n }\n\n // Sticky grey header (DS DsTable.jsx).\n .ds-table__head {\n position: sticky;\n top: 0;\n z-index: 10;\n\n background-color: colors.$slate-bg;\n }\n\n th {\n // DS thStyle: 4px 16px, full hairline border, 30px tall.\n padding: s.spacing(0.5) s.spacing(2);\n height: 30px; // DS DsTable.jsx thStyle\n\n text-align: left;\n\n color: colors.$slate-main;\n font-weight: t.$weight-bold;\n font-size: t.font-size('body');\n\n border: 1px solid colors.$slate-tint;\n\n background-color: colors.$slate-bg;\n }\n\n .ds-table__select-all {\n width: 52px; // DS: leading checkbox column\n text-align: center;\n }\n\n // Compound body pins are DsCell (its own scope) — reach them via :deep to\n // drive the same scroll-toggled edge shadow. A single pin per side is the\n // clean case; stacking multiple pins per side casts a shadow per cell.\n :deep(.ds-cell.is-pinned) {\n transition: box-shadow m.$duration-default m.$easing;\n }\n .ds-table__wrapper.is-scrolled-start :deep(.ds-cell.is-pinned--left) {\n box-shadow: sh.$shadow-pinned-left;\n }\n .ds-table__wrapper.is-scrolled-end :deep(.ds-cell.is-pinned--right) {\n box-shadow: sh.$shadow-pinned-right;\n }\n }\n}\n</style>\n","// DsInput — the DS \"Input\": single-line field. Prop vocabulary mirrors Input.jsx.\n// Border: primary-main at rest → accent on focus, coral on error. Text goes BOLD\n// on focus (DS Input.jsx: fontWeight focus ? 700 : 400). Sizes are the DS input\n// heights. (DsTextarea intentionally does NOT bold on focus — matching its source.)\n\nexport const DS_INPUT_TYPES = {\n TEXT: 'text',\n EMAIL: 'email',\n PASSWORD: 'password',\n TEL: 'tel',\n NUMBER: 'number',\n SEARCH: 'search',\n URL: 'url',\n} as const;\nexport type DsInputType = (typeof DS_INPUT_TYPES)[keyof typeof DS_INPUT_TYPES];\n\nexport const DS_INPUT_SIZES = {\n SM: 'sm', // 36px\n MD: 'md', // 48px\n} as const;\nexport type DsInputSize = (typeof DS_INPUT_SIZES)[keyof typeof DS_INPUT_SIZES];\n","<template>\n <div\n class=\"ds-input\"\n :class=\"[\n `ds-input--${size}`,\n { 'is-error': error, 'is-disabled': disabled, 'is-search': isSearch },\n ]\"\n @focusout=\"onFieldFocusOut\"\n >\n <div class=\"ds-input__wrap\">\n <i v-if=\"isSearch\" class=\"material-icons ds-input__search-icon\" aria-hidden=\"true\">search</i>\n\n <input\n :id=\"id || undefined\"\n ref=\"inputEl\"\n v-model=\"model\"\n class=\"ds-input__control\"\n :class=\"{ 'has-suffix': hasSuffix }\"\n :type=\"resolvedType\"\n :placeholder=\"placeholder\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"disabled\"\n :name=\"name || undefined\"\n :autocomplete=\"autocomplete || undefined\"\n :aria-invalid=\"error || undefined\"\n :aria-describedby=\"error && errorMessage && id ? `${id}-error` : undefined\"\n @focus=\"emit(FOCUS, $event)\"\n @blur=\"emit(BLUR, $event)\"\n @change=\"emit(CHANGE, $event)\"\n />\n\n <div v-if=\"hasSuffix\" class=\"ds-input__suffix\">\n <slot name=\"suffix\" />\n\n <i\n v-if=\"error && showErrorIcon\"\n class=\"material-icons ds-input__error-icon\"\n aria-hidden=\"true\"\n >warning</i\n >\n\n <button\n v-else-if=\"hasRevealToggle\"\n type=\"button\"\n class=\"ds-input__toggle\"\n :disabled=\"disabled\"\n :aria-label=\"toggleLabel || undefined\"\n :aria-pressed=\"reveal\"\n @click=\"toggleReveal\"\n >\n <i class=\"material-icons\" aria-hidden=\"true\">{{\n reveal ? 'visibility' : 'visibility_off'\n }}</i>\n </button>\n </div>\n </div>\n\n <p v-if=\"error && errorMessage\" :id=\"id ? `${id}-error` : undefined\" class=\"ds-input__error\">\n {{ errorMessage }}\n </p>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, nextTick, ref, useSlots, type PropType } from 'vue';\nimport { FOCUS, BLUR, CHANGE, FIELD_BLUR } from '@shared/constants/events';\nimport {\n DS_INPUT_TYPES,\n DS_INPUT_SIZES,\n type DsInputType,\n type DsInputSize,\n} from './DsInput.types';\n\nconst model = defineModel<string>({ default: '' });\n\nconst props = defineProps({\n type: { type: String as PropType<DsInputType>, default: DS_INPUT_TYPES.TEXT },\n size: { type: String as PropType<DsInputSize>, default: DS_INPUT_SIZES.SM },\n placeholder: { type: String, default: '' },\n disabled: Boolean,\n error: Boolean,\n errorMessage: { type: String, default: '' },\n showErrorIcon: Boolean,\n name: { type: String, default: '' },\n id: { type: String, default: '' },\n autocomplete: { type: String, default: '' },\n // Accessible name for the password visibility toggle.\n toggleLabel: { type: String, default: '' },\n // Native number-input constraints — only meaningful for type=\"number\", forwarded\n // to the real <input>. String is allowed so `step=\"any\"` is expressible.\n min: { type: [Number, String], default: undefined },\n max: { type: [Number, String], default: undefined },\n step: { type: [Number, String], default: undefined },\n});\n\nconst emit = defineEmits([FOCUS, BLUR, CHANGE, FIELD_BLUR]);\n\n// Direct handle to the native control for the imperative focus API below.\nconst inputEl = ref<HTMLInputElement>();\n\n// Real \"field exit\": the native @blur above fires on every focus loss —including\n// when focus moves to this component's OWN password toggle— because `blur` does\n// not bubble and can't see the wider context. `focusout` DOES bubble, so on the\n// root we can read where focus went next (relatedTarget) and only emit when it\n// landed OUTSIDE the component. Same pattern as DsTooltip.onFocusOut.\nfunction onFieldFocusOut(e: FocusEvent) {\n const next = e.relatedTarget as Node | null;\n const root = e.currentTarget as HTMLElement | null;\n // Focus moved to a descendant (input ↔ its own toggle): still inside the field.\n if (next && root?.contains(next)) return;\n emit(FIELD_BLUR, e);\n}\n\nconst slots = useSlots();\nconst reveal = ref(false);\n// Guarded so a disabled field's toggle can't flip the reveal state (the native\n// `:disabled` already blocks the click; this is the belt-and-suspenders guard).\nfunction toggleReveal() {\n if (props.disabled) return;\n reveal.value = !reveal.value;\n}\nconst isPassword = computed(() => props.type === DS_INPUT_TYPES.PASSWORD);\nconst isSearch = computed(() => props.type === DS_INPUT_TYPES.SEARCH);\n// DS: password toggles text/password on reveal; search renders as a plain text\n// input (the search affordance is the left icon + styling, not the native type).\nconst resolvedType = computed(() => {\n if (isPassword.value) return reveal.value ? DS_INPUT_TYPES.TEXT : DS_INPUT_TYPES.PASSWORD;\n if (isSearch.value) return DS_INPUT_TYPES.TEXT;\n return props.type;\n});\n// The reveal toggle renders only when a `toggleLabel` is provided: an icon-only\n// button with no accessible name would break the a11y contract, and the library\n// can't hardcode the label (no hardcoded user-facing text). No label → no toggle.\nconst hasRevealToggle = computed(() => isPassword.value && !!props.toggleLabel);\nconst hasSuffix = computed(\n () => !!slots.suffix || (props.error && props.showErrorIcon) || hasRevealToggle.value\n);\n\n// Imperative focus API — pure delegation to the native input's own DOM methods\n// (no store, no side effects beyond focus). The consuming app calls these via a\n// template ref, e.g. to focus the field after opening a panel.\ndefineExpose({\n focus: () => inputEl.value?.focus(),\n focusAtEnd: async () => {\n // Wait a tick so any pending v-model render has flushed before we measure.\n await nextTick();\n const el = inputEl.value;\n if (!el) return;\n el.focus();\n const n = el.value.length;\n // setSelectionRange throws on input types that don't support text selection\n // (email, number, …); focus() alone is the correct behavior for those.\n try {\n el.setSelectionRange(n, n);\n } catch {\n /* type doesn't support selection — focus is enough */\n }\n },\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n@use '../../assets/css/icons.scss' as i;\n\n.ds-input {\n display: block;\n width: 100%;\n\n &__wrap {\n position: relative;\n width: 100%;\n }\n\n &__control {\n box-sizing: border-box;\n width: 100%;\n padding: 0 16px;\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n font-weight: t.$weight-regular;\n line-height: 20px;\n\n background-color: colors.$white;\n\n /* Border: primary-main at rest → accent on focus (see precedence below). */\n border: 1px solid colors.$slate-main;\n border-radius: r.$radius-control;\n outline: none;\n\n transition: border-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n &.has-suffix {\n padding-right: 50px;\n }\n\n &:focus {\n border-color: colors.$accent-main;\n font-weight: t.$weight-bold; // DS: text goes bold on focus\n }\n }\n\n &__suffix {\n position: absolute;\n top: 0;\n right: 0;\n\n display: flex;\n align-items: center;\n gap: 4px;\n\n height: 100%;\n padding: 0 16px;\n\n color: colors.$slate-soft;\n }\n\n &__toggle {\n display: inline-flex;\n padding: 0;\n\n color: inherit;\n\n background: transparent;\n border: 0;\n\n cursor: pointer;\n\n &:disabled {\n cursor: not-allowed;\n }\n }\n\n &__error-icon {\n color: colors.$error-main;\n }\n\n &__error {\n margin-top: 2px;\n\n color: colors.$error-main;\n font-family: t.$font-family;\n font-weight: t.$weight-bold;\n font-size: t.font-size('caption');\n line-height: 18px;\n }\n\n /* ---------- Sizes (DS input heights + icon sizes) ---------- */\n &--sm {\n .ds-input__control {\n height: 36px;\n }\n .material-icons {\n font-size: i.icon-size(18); // DS Input.jsx: suffix glyph is 18 at sm (24 at md)\n }\n }\n &--md {\n .ds-input__control {\n height: 48px;\n }\n .material-icons {\n font-size: i.icon-size(24);\n }\n }\n\n /* ---------- Search type (DS): left magnifier, subtle grey field, borderless\n until focus, and NOT bold-on-focus (the DS excludes search from the bold rule).\n Placed before States so is-error / is-disabled still win. ---------- */\n &.is-search {\n .ds-input__search-icon {\n position: absolute;\n top: 50%;\n left: 16px;\n\n color: colors.$slate-soft;\n font-size: i.icon-size(20);\n\n transform: translateY(-50%);\n pointer-events: none;\n }\n .ds-input__control {\n padding-left: 48px;\n\n background-color: colors.$slate-bg;\n border-color: transparent;\n }\n .ds-input__control:focus {\n font-weight: t.$weight-regular; // DS: search stays regular on focus\n border-color: colors.$accent-main;\n }\n }\n\n /* ---------- States (precedence: rest → focus → disabled → error) ---------- */\n &.is-disabled .ds-input__control {\n color: colors.$slate-mid;\n background-color: colors.$slate-bg;\n border-color: colors.$slate-mid;\n cursor: not-allowed;\n }\n &.is-error .ds-input__control,\n &.is-error .ds-input__control:focus {\n border-color: colors.$error-main;\n }\n}\n</style>\n","// DsTextarea — the DS \"Textarea\": multi-line variant of DsInput, same border\n// treatment (primary-main at rest → accent on focus, coral on error). Prop\n// vocabulary mirrors Textarea.jsx. No variant enums; this documents the props.\n\n// CSS `resize` keyword the control accepts (default vertical).\nexport const DS_TEXTAREA_RESIZE = {\n NONE: 'none',\n VERTICAL: 'vertical',\n HORIZONTAL: 'horizontal',\n BOTH: 'both',\n} as const;\nexport type DsTextareaResize = (typeof DS_TEXTAREA_RESIZE)[keyof typeof DS_TEXTAREA_RESIZE];\n\nexport interface DsTextareaProps {\n modelValue?: string;\n placeholder?: string;\n rows?: number;\n error?: boolean;\n errorMessage?: string;\n disabled?: boolean;\n resize?: DsTextareaResize;\n}\n","<template>\n <div\n class=\"ds-textarea\"\n :class=\"[`ds-textarea--resize-${resize}`, { 'is-error': error, 'is-disabled': disabled }]\"\n v-bind=\"rootAttrs\"\n >\n <textarea\n v-bind=\"controlAttrs\"\n :id=\"id || undefined\"\n v-model=\"model\"\n class=\"ds-textarea__control\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n :disabled=\"disabled\"\n :name=\"name || undefined\"\n :aria-invalid=\"error || undefined\"\n :aria-describedby=\"error && errorMessage && id ? `${id}-error` : undefined\"\n @focus=\"emit(FOCUS, $event)\"\n @blur=\"emit(BLUR, $event)\"\n />\n <p v-if=\"error && errorMessage\" :id=\"id ? `${id}-error` : undefined\" class=\"ds-textarea__error\">\n {{ errorMessage }}\n </p>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, useAttrs, type PropType } from 'vue';\nimport { FOCUS, BLUR } from '@shared/constants/events';\nimport { DS_TEXTAREA_RESIZE, type DsTextareaResize } from './DsTextarea.types';\n\n// Split fallthrough attrs: `class`/`style`/`data-*` describe the field wrapper\n// (layout, spacing, test hooks) and stay on the root, exactly as before; native\n// control attrs (maxlength, autocomplete, spellcheck, autofocus, aria-*, listeners…)\n// go to the real <textarea> — where they previously landed uselessly on the div.\ndefineOptions({ inheritAttrs: false });\n\nconst model = defineModel<string>({ default: '' });\n\ndefineProps({\n placeholder: { type: String, default: '' },\n rows: { type: Number, default: 4 },\n disabled: Boolean,\n error: Boolean,\n errorMessage: { type: String, default: '' },\n name: { type: String, default: '' },\n id: { type: String, default: '' },\n // CSS resize behavior of the control (default vertical, mirrors the DS).\n resize: {\n type: String as PropType<DsTextareaResize>,\n default: DS_TEXTAREA_RESIZE.VERTICAL,\n },\n});\n\nconst emit = defineEmits([FOCUS, BLUR]);\n\nconst attrs = useAttrs();\nconst isRootAttr = (k: string) => k === 'class' || k === 'style' || k.startsWith('data-');\nconst rootAttrs = computed(() =>\n Object.fromEntries(Object.entries(attrs).filter(([k]) => isRootAttr(k)))\n);\nconst controlAttrs = computed(() =>\n Object.fromEntries(Object.entries(attrs).filter(([k]) => !isRootAttr(k)))\n);\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n\n.ds-textarea {\n display: block;\n width: 100%;\n\n &__control {\n box-sizing: border-box;\n width: 100%;\n padding: 12px 16px;\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n line-height: 20px;\n\n background-color: colors.$white;\n\n border: 1px solid colors.$slate-main;\n border-radius: r.$radius-control;\n outline: none;\n\n resize: vertical;\n transition: border-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n &:focus {\n border-color: colors.$accent-main;\n }\n }\n\n // Resize behavior — `vertical` is the base rule above; these override it.\n &--resize-none .ds-textarea__control {\n resize: none;\n }\n &--resize-horizontal .ds-textarea__control {\n resize: horizontal;\n }\n &--resize-both .ds-textarea__control {\n resize: both;\n }\n\n &__error {\n margin-top: 2px;\n\n color: colors.$error-main;\n font-family: t.$font-family;\n font-weight: t.$weight-bold;\n font-size: t.font-size('caption');\n line-height: 18px;\n }\n\n &.is-disabled .ds-textarea__control {\n color: colors.$slate-mid;\n background-color: colors.$slate-bg;\n border-color: colors.$slate-mid;\n cursor: not-allowed;\n }\n &.is-error .ds-textarea__control,\n &.is-error .ds-textarea__control:focus {\n border-color: colors.$error-main;\n }\n}\n</style>\n","// DsAlert — the DS inline banner (Alert.jsx). type × effect (light tint / dark solid).\n\nexport const DS_ALERT_TYPES = {\n SUCCESS: 'success',\n INFO: 'info',\n WARNING: 'warning',\n ERROR: 'error',\n} as const;\nexport type DsAlertType = (typeof DS_ALERT_TYPES)[keyof typeof DS_ALERT_TYPES];\n\nexport const DS_ALERT_EFFECTS = {\n LIGHT: 'light', // tinted: family tint bg + mid border + shadow text\n DARK: 'dark', // solid: family shadow bg + white text\n} as const;\nexport type DsAlertEffect = (typeof DS_ALERT_EFFECTS)[keyof typeof DS_ALERT_EFFECTS];\n\n// Material Icon per type (leading icon when showIcon).\nexport const DS_ALERT_ICONS: Record<DsAlertType, string> = {\n success: 'check_circle',\n info: 'info',\n warning: 'warning',\n error: 'error',\n};\n","<template>\n <transition name=\"ds-alert-fade\">\n <div\n v-if=\"visible\"\n class=\"ds-alert\"\n :class=\"[`ds-alert--${type}`, `ds-alert--${effect}`, { 'is-center': center }]\"\n role=\"alert\"\n >\n <i v-if=\"showIcon\" class=\"material-icons ds-alert__icon\" aria-hidden=\"true\">{{ icon }}</i>\n\n <div class=\"ds-alert__content\">\n <div\n v-if=\"hasTitle\"\n class=\"ds-alert__title\"\n :class=\"{ 'has-body': hasBody, 'is-bold': hasBody || boldTitle }\"\n >\n <slot name=\"title\">{{ title }}</slot>\n </div>\n <div v-if=\"hasBody\" class=\"ds-alert__body\">\n <slot>{{ description }}</slot>\n </div>\n </div>\n\n <button\n v-if=\"closable\"\n type=\"button\"\n class=\"ds-alert__close\"\n :aria-label=\"closeLabel || undefined\"\n @click=\"onClose\"\n >\n <i class=\"material-icons\" aria-hidden=\"true\">close</i>\n </button>\n </div>\n </transition>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, useSlots, type PropType } from 'vue';\nimport { CLOSE } from '@shared/constants/events';\nimport {\n DS_ALERT_TYPES,\n DS_ALERT_EFFECTS,\n DS_ALERT_ICONS,\n type DsAlertType,\n type DsAlertEffect,\n} from './DsAlert.types';\n\nconst props = defineProps({\n type: { type: String as PropType<DsAlertType>, default: DS_ALERT_TYPES.INFO },\n effect: { type: String as PropType<DsAlertEffect>, default: DS_ALERT_EFFECTS.LIGHT },\n title: { type: String, default: '' },\n description: { type: String, default: '' },\n showIcon: Boolean,\n closable: Boolean,\n center: Boolean,\n // Bold the title even without a body (DS: fontWeight = body || boldTitle ? 700 : 400).\n boldTitle: Boolean,\n closeLabel: { type: String, default: '' },\n});\n\nconst emit = defineEmits([CLOSE]);\n\n// Visibility is opt-in controllable: bind `v-model:visible` to reset/reopen the\n// alert declaratively from the parent. Without a binding it defaults to `true`\n// and behaves as internal state — the X still self-closes and emits `close`.\nconst visible = defineModel('visible', { type: Boolean, default: true });\n\nconst slots = useSlots();\nconst icon = computed(() => DS_ALERT_ICONS[props.type]);\nconst hasBody = computed(() => !!slots.default || props.description.length > 0);\n// Title renders from the #title slot (rich content) or the `title` prop.\nconst hasTitle = computed(() => !!slots.title || props.title.length > 0);\n\nfunction onClose() {\n visible.value = false;\n emit(CLOSE);\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/icons.scss' as i;\n@use '../../assets/css/motion.scss' as m;\n\n.ds-alert {\n position: relative;\n\n display: flex;\n align-items: center;\n\n padding: 8px 16px;\n\n font-family: t.$font-family;\n\n border: 1px solid transparent;\n border-radius: r.$radius-control;\n\n &.is-center {\n justify-content: center;\n }\n\n &__icon {\n margin-right: 8px;\n // DS Alert.jsx: status glyph is 16 (md-20 class, fontSize:16 override).\n font-size: i.icon-size(16);\n }\n\n &__content {\n flex: 1;\n padding: 0 8px;\n }\n\n &__title {\n font-size: t.font-size('small');\n font-weight: t.$weight-regular;\n line-height: 18px;\n\n // Bold when a body follows OR boldTitle is set. Kept separate from `has-body`\n // (which only drives the body's margin-top sibling selector below).\n &.is-bold {\n font-weight: t.$weight-bold;\n }\n }\n\n &__body {\n font-size: t.font-size('caption');\n }\n\n // DS: body margin-top is 5 only when a title precedes it, 0 otherwise.\n &__title.has-body + .ds-alert__body {\n margin-top: 5px;\n }\n\n &__close {\n position: absolute;\n top: 12px;\n right: 15px;\n\n display: inline-flex;\n padding: 0;\n\n color: inherit;\n\n background: transparent;\n border: 0;\n\n cursor: pointer;\n\n .material-icons {\n font-size: i.icon-size(16);\n }\n }\n\n /* ---------- light (tinted) — bg tint + mid border + shadow text ---------- */\n &--light {\n &.ds-alert--success {\n color: colors.$success-shadow;\n border-color: colors.$success-mid;\n background-color: colors.$success-tint;\n }\n &.ds-alert--info {\n color: colors.$info-shadow;\n border-color: colors.$info-mid;\n background-color: colors.$info-tint;\n }\n &.ds-alert--warning {\n color: colors.$warning-shadow;\n border-color: colors.$warning-mid;\n background-color: colors.$warning-tint;\n }\n &.ds-alert--error {\n color: colors.$error-shadow;\n border-color: colors.$error-mid;\n background-color: colors.$error-tint;\n }\n }\n\n /* ---------- dark (solid) — bg shadow + white text ---------- */\n &--dark {\n color: colors.$white;\n border-color: transparent;\n\n &.ds-alert--success {\n background-color: colors.$success-shadow;\n }\n &.ds-alert--info {\n background-color: colors.$info-shadow;\n }\n &.ds-alert--warning {\n background-color: colors.$warning-shadow;\n }\n &.ds-alert--error {\n background-color: colors.$error-shadow;\n }\n }\n}\n\n/* Fade on mount/dismiss (the root v-if). Opacity only — no color/spacing literal. */\n.ds-alert-fade-enter-active,\n.ds-alert-fade-leave-active {\n transition: opacity m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n}\n.ds-alert-fade-enter-from,\n.ds-alert-fade-leave-to {\n opacity: 0;\n}\n</style>\n","// DsNotification — the DS toast card (Notification.jsx). Fixed-position, 3 types.\n\nexport const DS_NOTIFICATION_TYPES = {\n SUCCESS: 'success',\n INFO: 'info',\n WARNING: 'warning',\n ERROR: 'error',\n} as const;\nexport type DsNotificationType = (typeof DS_NOTIFICATION_TYPES)[keyof typeof DS_NOTIFICATION_TYPES];\n\nexport const DS_NOTIFICATION_POSITIONS = {\n TOP_LEFT: 'top-left',\n TOP_RIGHT: 'top-right',\n BOTTOM_LEFT: 'bottom-left',\n BOTTOM_RIGHT: 'bottom-right',\n} as const;\nexport type DsNotificationPosition =\n (typeof DS_NOTIFICATION_POSITIONS)[keyof typeof DS_NOTIFICATION_POSITIONS];\n\n// Material Icon per type. `error` uses the filled `error` glyph (circle) per the live\n// design prototype (ApiApp.jsx ToastCard) — the older _ds_bundle.js used `warning` here.\nexport const DS_NOTIFICATION_ICONS: Record<DsNotificationType, string> = {\n success: 'check_circle',\n info: 'mode_edit',\n warning: 'warning',\n error: 'error',\n};\n\n// `target` del <a> del link. Default `_blank` (comportamiento histórico, se\n// mantiene para no romper). Usá `_self` para navegar en la misma pestaña; para\n// navegación in-app (SPA, sin recarga) escuchá el evento `navigate` y llamá al\n// router de la app. `rel=\"noopener noreferrer\"` se aplica solo con `_blank`.\nexport const DS_NOTIFICATION_LINK_TARGETS = {\n BLANK: '_blank',\n SELF: '_self',\n PARENT: '_parent',\n TOP: '_top',\n} as const;\nexport type DsNotificationLinkTarget =\n (typeof DS_NOTIFICATION_LINK_TARGETS)[keyof typeof DS_NOTIFICATION_LINK_TARGETS];\n","<template>\n <Teleport to=\"body\">\n <Transition name=\"ds-notification\" appear>\n <div v-if=\"visible\" class=\"ds-notification\" :class=\"`ds-notification--${position}`\">\n <div\n class=\"ds-notification__card\"\n :class=\"`ds-notification__card--${type}`\"\n role=\"alert\"\n @mouseenter=\"pause\"\n @mouseleave=\"resume\"\n >\n <i class=\"material-icons ds-notification__icon\" aria-hidden=\"true\">{{ icon }}</i>\n\n <div class=\"ds-notification__content\">\n <span v-if=\"title\" class=\"ds-notification__title\">{{ title }}</span>\n <p v-if=\"description\" class=\"ds-notification__desc\">{{ description }}</p>\n <a\n v-if=\"linkTitle\"\n class=\"ds-notification__link\"\n :href=\"link || undefined\"\n :target=\"linkTarget\"\n :rel=\"linkRel\"\n @click=\"onLinkClick\"\n >{{ linkTitle }}</a\n >\n </div>\n\n <button\n v-if=\"showClose\"\n type=\"button\"\n class=\"ds-notification__close\"\n :aria-label=\"closeLabel || undefined\"\n @click=\"onClose\"\n >\n <i class=\"material-icons\" aria-hidden=\"true\">close</i>\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, type PropType } from 'vue';\nimport { CLOSE, NAVIGATE } from '@shared/constants/events';\nimport {\n DS_NOTIFICATION_TYPES,\n DS_NOTIFICATION_POSITIONS,\n DS_NOTIFICATION_ICONS,\n DS_NOTIFICATION_LINK_TARGETS,\n type DsNotificationType,\n type DsNotificationPosition,\n type DsNotificationLinkTarget,\n} from './DsNotification.types';\n\nconst props = defineProps({\n type: { type: String as PropType<DsNotificationType>, default: DS_NOTIFICATION_TYPES.INFO },\n position: {\n type: String as PropType<DsNotificationPosition>,\n default: DS_NOTIFICATION_POSITIONS.BOTTOM_LEFT,\n },\n title: { type: String, default: '' },\n description: { type: String, default: '' },\n linkTitle: { type: String, default: '' },\n link: { type: String, default: '' },\n // `target` del <a> del link. Default `_blank` para no romper el comportamiento\n // histórico. Con `_blank` se emite `rel=\"noopener noreferrer\"` (seguridad). Para\n // navegación in-app (SPA) escuchá `navigate` y enrutá con el router de la app.\n linkTarget: {\n type: String as PropType<DsNotificationLinkTarget>,\n default: DS_NOTIFICATION_LINK_TARGETS.BLANK,\n },\n showClose: { type: Boolean, default: true },\n // Accessible name for the icon-only close button — provide it whenever\n // `showClose` is on, or the button has no name for screen readers (as with any\n // icon-only control, cf. DsButton circle/square requiring `ariaLabel`). The\n // library never hardcodes copy; the consuming app owns i18n.\n closeLabel: { type: String, default: '' },\n // Auto-dismiss delay in ms (DS default 3500). 0 disables it. The timer pauses\n // while the pointer is over the card and resumes with the remaining time.\n duration: { type: Number, default: 3500 },\n});\n\nconst emit = defineEmits([CLOSE, NAVIGATE]);\n\nconst visible = ref(true);\nconst icon = computed(() => DS_NOTIFICATION_ICONS[props.type]);\n\n// `noopener noreferrer` solo cuando el link abre en otra pestaña (`_blank`):\n// evita que la página destino acceda a `window.opener` y filtre el referrer.\nconst linkRel = computed(() =>\n props.linkTarget === DS_NOTIFICATION_LINK_TARGETS.BLANK ? 'noopener noreferrer' : undefined\n);\n\nlet timer: ReturnType<typeof setTimeout> | undefined;\nlet startedAt = 0;\nlet remaining = props.duration;\n\nfunction clearTimer() {\n if (timer) {\n clearTimeout(timer);\n timer = undefined;\n }\n}\n\nfunction schedule(ms: number) {\n clearTimer();\n if (props.duration <= 0 || !visible.value) return;\n startedAt = Date.now();\n timer = setTimeout(onClose, ms);\n}\n\nfunction pause() {\n if (!timer) return;\n remaining -= Date.now() - startedAt;\n clearTimer();\n}\n\nfunction resume() {\n schedule(remaining > 0 ? remaining : props.duration);\n}\n\nfunction onClose() {\n clearTimer();\n visible.value = false;\n emit(CLOSE);\n}\n\n// El link es un <a href> real (a11y: focuseable, teclado, fallback sin JS). Emitimos\n// el MouseEvent para que el consumidor pueda `preventDefault()` y enrutar in-app con\n// su router; sin listener, la navegación nativa del <a> sigue funcionando igual.\nfunction onLinkClick(event: MouseEvent) {\n emit(NAVIGATE, event);\n}\n\nonMounted(() => schedule(props.duration));\nonBeforeUnmount(clearTimer);\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/shadow.scss' as sh;\n@use '../../assets/css/icons.scss' as i;\n@use '../../assets/css/motion.scss' as m;\n\n.ds-notification {\n position: fixed;\n z-index: 2000;\n\n &--top-left {\n top: 0;\n left: 0;\n }\n &--top-right {\n top: 0;\n right: 0;\n }\n &--bottom-left {\n bottom: 0;\n left: 0;\n }\n &--bottom-right {\n bottom: 0;\n right: 0;\n }\n\n &__card {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n\n max-width: 331px;\n margin: 24px;\n padding: 16px;\n\n border: 1px solid transparent;\n border-radius: r.$radius-card-lg;\n\n box-shadow: sh.$shadow-soft;\n\n // Success is the DS default/most-used type. Icons use the darker -shadow step\n // for legibility on the tint (warning/error/success). NOTE: the _ds_bundle.js\n // export uses the -main step for icons — this -shadow treatment tracks the\n // live design, pending a visual confirm against claude.ai/design.\n &--success {\n background-color: colors.$success-tint;\n border-color: rgba(colors.$success-main, 0.7);\n\n .ds-notification__icon {\n color: colors.$success-shadow;\n }\n }\n &--info {\n background-color: colors.$info-tint;\n border-color: rgba(colors.$info-main, 0.7);\n\n .ds-notification__icon {\n color: colors.$info-main;\n }\n }\n &--warning {\n background-color: colors.$warning-tint;\n border-color: rgba(colors.$warning-main, 0.7);\n\n .ds-notification__icon {\n color: colors.$warning-shadow;\n }\n }\n &--error {\n background-color: colors.$error-tint;\n border-color: rgba(colors.$error-main, 0.7);\n\n .ds-notification__icon {\n color: colors.$error-shadow;\n }\n }\n }\n\n &__icon {\n font-size: i.icon-size(20);\n }\n\n &__content {\n display: flex;\n flex-direction: column;\n\n color: colors.$slate-shadow;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n line-height: 21px;\n }\n\n &__title {\n font-weight: t.$weight-bold;\n }\n\n &__desc {\n margin: 0;\n padding-top: 8px;\n }\n\n &__link {\n padding-top: 8px;\n\n color: colors.$accent-main;\n text-decoration: underline;\n }\n\n &__close {\n display: inline-flex;\n margin-left: auto;\n padding: 0;\n\n color: colors.$slate-soft;\n\n background: transparent;\n border: 0;\n\n cursor: pointer;\n\n .material-icons {\n font-size: i.icon-size(20);\n }\n }\n}\n\n/* Enter: the card fades + slides up (~0.24s toast duration). */\n.ds-notification-enter-active {\n transition: opacity m.$duration-toast m.$easing;\n\n .ds-notification__card {\n transition: transform m.$duration-toast m.$easing;\n }\n\n @include m.reduce-motion {\n transition: none;\n\n .ds-notification__card {\n transition: none;\n }\n }\n}\n.ds-notification-enter-from {\n opacity: 0;\n\n .ds-notification__card {\n transform: translateY(8px);\n }\n}\n</style>\n","<template>\n <Teleport to=\"body\" :disabled=\"!fixed\">\n <Transition name=\"ds-modal\">\n <div\n v-if=\"open\"\n class=\"ds-modal\"\n :class=\"{ 'is-fixed': fixed }\"\n :style=\"fixed ? { paddingTop: top } : undefined\"\n @click=\"onBackdrop\"\n >\n <div\n ref=\"dialogRef\"\n class=\"ds-modal__dialog\"\n :class=\"{ 'is-center': center }\"\n role=\"dialog\"\n :aria-modal=\"fixed ? 'true' : 'false'\"\n :aria-labelledby=\"title ? titleId : undefined\"\n :aria-label=\"title ? undefined : 'dialog'\"\n tabindex=\"-1\"\n :style=\"dialogStyle\"\n @click.stop\n >\n <div v-if=\"title || showClose\" class=\"ds-modal__header\">\n <span :id=\"titleId\" class=\"ds-modal__title\">{{ title }}</span>\n <DsButton\n v-if=\"showClose\"\n variant=\"tertiary\"\n circle\n left-icon=\"close\"\n :aria-label=\"closeLabel || undefined\"\n @click=\"emit(CLOSE)\"\n />\n </div>\n\n <div class=\"ds-modal__body\"><slot /></div>\n\n <div v-if=\"$slots.footer\" class=\"ds-modal__footer\"><slot name=\"footer\" /></div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, watch, nextTick, onBeforeUnmount, useId } from 'vue';\nimport { DsButton } from '@shared/ui/DsButton';\nimport { CLOSE } from '@shared/constants/events';\n\nconst props = defineProps({\n open: Boolean,\n title: { type: String, default: '' },\n showClose: { type: Boolean, default: true },\n width: { type: String, default: '440px' },\n // Explicit dialog height (any CSS length). Unset = content height.\n height: { type: String, default: '' },\n // Top offset of the dialog inside the fixed backdrop.\n top: { type: String, default: '15vh' },\n // Center the body text.\n center: Boolean,\n // false = render in-flow (no backdrop / portal / scroll-lock) for specimen cards.\n fixed: { type: Boolean, default: true },\n closeOnBackdrop: { type: Boolean, default: true },\n // Symmetric with closeOnBackdrop: a non-dismissable flow (closeOnBackdrop=false)\n // can also opt out of Escape-to-close. Default true (Escape closes).\n closeOnEscape: { type: Boolean, default: true },\n // Accessible name for the icon-only close button; English default (app owns i18n),\n // matching DsDataCard so the control is never unnamed.\n closeLabel: { type: String, default: 'Close' },\n});\n\nconst emit = defineEmits([CLOSE]);\n\nconst dialogRef = ref<HTMLElement | null>(null);\nlet lastFocused: HTMLElement | null = null;\n\n// SSR-safe id so the dialog is labelled by its visible title (aria-labelledby) instead of\n// duplicating the string via aria-label.\nconst titleId = useId();\n\nconst dialogStyle = computed(() => ({\n width: props.width,\n ...(props.height ? { height: props.height } : {}),\n}));\n\nfunction onBackdrop() {\n if (props.fixed && props.closeOnBackdrop) emit(CLOSE);\n}\n\nfunction focusables(): HTMLElement[] {\n if (!dialogRef.value) return [];\n const sel =\n 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n return Array.from(dialogRef.value.querySelectorAll<HTMLElement>(sel)).filter(\n (el) => el.offsetParent !== null\n );\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (e.key === 'Escape') {\n if (props.closeOnEscape) emit(CLOSE);\n return;\n }\n if (e.key !== 'Tab') return;\n const f = focusables();\n if (f.length === 0) {\n e.preventDefault();\n dialogRef.value?.focus();\n return;\n }\n const first = f[0];\n const last = f[f.length - 1];\n if (e.shiftKey && document.activeElement === first) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && document.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n}\n\n// Portal effects (focus trap + scroll lock + Esc listener) apply only while the modal\n// is open AND fixed. Watch the combined condition so flipping EITHER off — even without\n// closing — tears them down (no dangling listener / body scroll-lock).\nfunction activateModal() {\n lastFocused = document.activeElement as HTMLElement;\n document.addEventListener('keydown', onKeydown);\n document.body.style.overflow = 'hidden';\n nextTick(() => (focusables()[0] ?? dialogRef.value)?.focus());\n}\n\nfunction deactivateModal(restoreFocus = true) {\n document.removeEventListener('keydown', onKeydown);\n document.body.style.overflow = '';\n if (restoreFocus) lastFocused?.focus?.();\n}\n\nwatch(\n () => props.open && props.fixed,\n (active, wasActive) => {\n if (typeof document === 'undefined') return;\n if (active) activateModal();\n else if (wasActive) deactivateModal();\n },\n { immediate: true }\n);\n\nonBeforeUnmount(() => {\n if (typeof document === 'undefined') return;\n deactivateModal(false);\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/shadow.scss' as sh;\n@use '../../assets/css/motion.scss' as m;\n\n.ds-modal {\n // In-flow (fixed=false): the wrapper collapses so the dialog joins the parent flow.\n display: contents;\n\n // Portal (fixed=true): full-screen backdrop that centers the dialog. padding-top comes\n // from the `top` prop (inline), so the offset is configurable without hardcoding it here.\n &.is-fixed {\n position: fixed;\n inset: 0;\n z-index: 2001;\n\n display: flex;\n align-items: flex-start;\n justify-content: center;\n\n padding-right: 16px;\n padding-bottom: 50px;\n padding-left: 16px;\n overflow: auto;\n\n background: colors.$backdrop; // grey-10 @ 50%, no blur\n }\n\n &__dialog {\n max-width: 100%;\n\n // Horizontal frame only. Vertical spacing is owned by header/body/footer so their\n // symmetric 8px padding is not doubled by an outer frame (fixes off-center header/footer).\n padding: 0 8px;\n\n background: colors.$white;\n\n border-radius: r.$radius-card-lg; // DS readme: 8px (the MCP/API mockup's 6px reconciles up to 8px)\n box-shadow: sh.$shadow-card;\n\n outline: none;\n\n &.is-center {\n text-align: center; // body inherits; header/footer are flex, so unaffected\n }\n }\n\n &__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n min-height: 36px;\n // 12px inline (like body/footer) → title + close button sit on the shared 20px column.\n padding: 8px 12px;\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n font-weight: t.$weight-bold;\n\n border-bottom: 1px solid colors.$slate-tint;\n\n text-align: initial; // never centered, even under .is-center\n }\n\n &__body {\n // 12px inline matches the header's 12px so the body text lines up under the title\n // (both land 20px from the dialog edge). 20px block keeps the vertical breathing room.\n padding: 20px 12px;\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n }\n\n &__footer {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n\n // 8px block keeps the footer vertically centered; 12px inline aligns the actions\n // with the body's right edge (the shared 20px column).\n padding: 8px 12px;\n\n border-top: 1px solid colors.$slate-tint;\n }\n}\n\n/* Enter/leave (portal only): backdrop fades, dialog fades + lifts (0.3s dialog duration). */\n.ds-modal-enter-active.is-fixed,\n.ds-modal-leave-active.is-fixed {\n transition: opacity m.$duration-dialog m.$easing;\n\n .ds-modal__dialog {\n transition: transform m.$duration-dialog m.$easing;\n }\n\n @include m.reduce-motion {\n transition: none;\n\n .ds-modal__dialog {\n transition: none;\n }\n }\n}\n.ds-modal-enter-from.is-fixed,\n.ds-modal-leave-to.is-fixed {\n opacity: 0;\n\n .ds-modal__dialog {\n transform: translateY(-8px);\n }\n}\n</style>\n","// DsTooltip — dark slate bubble wrapping a trigger (Tooltip.jsx). 4-way placement.\n// Pure-CSS positioning (zero-dep, no floating-ui). content via `content` prop or #content slot.\n\nexport const DS_TOOLTIP_PLACEMENTS = {\n TOP: 'top',\n BOTTOM: 'bottom',\n LEFT: 'left',\n RIGHT: 'right',\n} as const;\nexport type DsTooltipPlacement = (typeof DS_TOOLTIP_PLACEMENTS)[keyof typeof DS_TOOLTIP_PLACEMENTS];\n","<template>\n <div\n class=\"ds-tooltip\"\n :class=\"{ 'ds-tooltip--block': block }\"\n @mouseenter=\"scheduleShow\"\n @mouseleave=\"onMouseLeave\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n @keydown.esc=\"hide\"\n >\n <!-- A <div> (not a <span>) so the trigger slot may hold block-root components\n without invalid HTML. Inline-flex by default (buttons/icons); pass `block`\n to wrap a full-width/block control (DsInput/DsTextarea) without squashing it. -->\n <slot />\n\n <Transition name=\"ds-tooltip-fade\">\n <div\n v-if=\"visible\"\n :id=\"tooltipId\"\n class=\"ds-tooltip__bubble\"\n :class=\"`ds-tooltip__bubble--${placement}`\"\n role=\"tooltip\"\n >\n <slot name=\"content\">{{ content }}</slot>\n </div>\n </Transition>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref, useId, useSlots, watch, type PropType } from 'vue';\nimport { DS_TOOLTIP_PLACEMENTS, type DsTooltipPlacement } from './DsTooltip.types';\n\nconst props = defineProps({\n // Bubble text. Rich content can be provided via the #content slot instead.\n content: { type: String, default: '' },\n placement: {\n type: String as PropType<DsTooltipPlacement>,\n default: DS_TOOLTIP_PLACEMENTS.TOP,\n },\n // Hover delay before showing, ms. Focus always shows immediately (no delay).\n delay: { type: Number, default: 300 },\n // Wrap a full-width/block trigger (DsInput/DsTextarea) — the wrapper becomes\n // `display: block` instead of the default inline-flex, so it doesn't squash the control.\n block: { type: Boolean, default: false },\n // Force the tooltip off regardless of content/slot. Lets a consumer show it only\n // under a condition (e.g. only-if-truncated) without emptying the content prop —\n // the #content slot is always truthy, so it's the only way to suppress that path.\n disabled: { type: Boolean, default: false },\n});\n\nconst slots = useSlots();\n\n// SSR-safe unique id (Vue 3.5); the bubble carries it and the focused trigger references it.\nconst tooltipId = useId();\n\nconst visible = ref(false);\n// Don't pop a bubble when disabled or when there is nothing to describe. Both\n// entry points (scheduleShow, onFocusIn) short-circuit on this.\nconst hasContent = computed(() => !props.disabled && (props.content.length > 0 || !!slots.content));\n\n// If it gets disabled while open, close it (hide() clears the timer + aria-describedby).\nwatch(\n () => props.disabled,\n (d) => {\n if (d) hide();\n }\n);\n\n// Hover and keyboard-focus are tracked separately: the tooltip only hides once BOTH\n// are false, so leaving with the mouse while still focused (or vice versa) keeps it open.\nlet hovering = false;\nlet focused = false;\nlet timer: ReturnType<typeof setTimeout> | null = null;\n\n// aria-describedby is set on the actually-focused element (not the wrapper) so a screen\n// reader announces the tooltip for the control it belongs to. We restore any prior value.\nlet describedEl: HTMLElement | null = null;\nlet prevDescribedBy: string | null = null;\n\nfunction clearTimer() {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n}\n\nfunction describe(el: HTMLElement | null) {\n if (!el) return;\n if (describedEl && describedEl !== el) undescribe();\n if (describedEl === el) return;\n describedEl = el;\n prevDescribedBy = el.getAttribute('aria-describedby');\n el.setAttribute(\n 'aria-describedby',\n prevDescribedBy ? `${prevDescribedBy} ${tooltipId}` : tooltipId\n );\n}\n\nfunction undescribe() {\n if (!describedEl) return;\n if (prevDescribedBy !== null) describedEl.setAttribute('aria-describedby', prevDescribedBy);\n else describedEl.removeAttribute('aria-describedby');\n describedEl = null;\n prevDescribedBy = null;\n}\n\n// Hover: show after `delay`.\nfunction scheduleShow() {\n if (!hasContent.value) return;\n hovering = true;\n clearTimer();\n timer = setTimeout(() => {\n visible.value = true;\n }, props.delay);\n}\n\nfunction onMouseLeave() {\n hovering = false;\n clearTimer(); // cancel a still-pending hover show\n if (!focused) hide();\n}\n\n// Focus: show immediately and wire aria-describedby onto the focused trigger.\nfunction onFocusIn(e: FocusEvent) {\n if (!hasContent.value) return;\n focused = true;\n clearTimer();\n visible.value = true;\n describe(e.target as HTMLElement | null);\n}\n\nfunction onFocusOut(e: FocusEvent) {\n // Focus moving to another element INSIDE the same wrapper (e.g. an input and its\n // own toggle button) isn't leaving the trigger — keep the tooltip open and let the\n // following focusin move aria-describedby to the new element.\n const next = e.relatedTarget as Node | null;\n const wrapper = e.currentTarget as HTMLElement | null;\n if (next && wrapper?.contains(next)) return;\n focused = false;\n if (!hovering) hide();\n}\n\nfunction hide() {\n clearTimer();\n visible.value = false;\n undescribe();\n}\n\nonBeforeUnmount(() => {\n clearTimer();\n undescribe();\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n\n.ds-tooltip {\n position: relative;\n display: inline-flex; // inline triggers (buttons/icons) — the DS default\n\n // Full-width/block triggers: don't impose inline layout (see `block` prop).\n &--block {\n display: block;\n }\n\n &__bubble {\n position: absolute;\n z-index: 10001; // above modals (2001); DS Tooltip.jsx zIndex 10001\n\n width: max-content;\n max-width: 260px;\n padding: 2px 8px;\n\n color: colors.$white;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n font-weight: t.$weight-bold;\n line-height: 20px;\n white-space: pre-line;\n\n background: colors.$slate-main; // DS --primary-main\n border-radius: r.$radius-control; // DS --radius-sm\n\n // 4-way placement, 6px gap from the trigger. Pure CSS — no floating-ui.\n &--top {\n bottom: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n }\n &--bottom {\n top: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n }\n &--right {\n top: 50%;\n left: calc(100% + 6px);\n transform: translateY(-50%);\n }\n &--left {\n top: 50%;\n right: calc(100% + 6px);\n transform: translateY(-50%);\n }\n }\n}\n\n/* Fade only — the positioning transform must not be animated (it would fight placement). */\n.ds-tooltip-fade-enter-active,\n.ds-tooltip-fade-leave-active {\n transition: opacity m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n}\n.ds-tooltip-fade-enter-from,\n.ds-tooltip-fade-leave-to {\n opacity: 0;\n}\n</style>\n","// DsVerificationIcon — status glyph for a contact data point.\n// 1:1 with the DS source (components/data/VerificationIcon.jsx):\n// verified === true -> \"verified\" (info-main, blue)\n// verified === false + email -> \"dangerous\" (error-main, coral)\n// verified === false + phone -> \"error\" (warning-main, orange)\n// verified == null (new) -> \"new_releases\" (slate-soft, grey)\n// `isContactData` flags emails (coral) vs phones (orange) when unverified.\n\nexport const DS_VERIFICATION_STATES = {\n VERIFIED: 'verified',\n UNVERIFIED: 'unverified',\n NEW: 'new',\n} as const;\nexport type DsVerificationState =\n (typeof DS_VERIFICATION_STATES)[keyof typeof DS_VERIFICATION_STATES];\n","<template>\n <i\n class=\"material-icons ds-verification-icon\"\n :class=\"[\n `ds-verification-icon--${status.state}`,\n { 'ds-verification-icon--email': isContactData },\n ]\"\n :style=\"{ fontSize: `${size}px` }\"\n role=\"img\"\n :aria-label=\"label || status.label\"\n :title=\"label || status.label\"\n >\n <slot>{{ status.icon }}</slot>\n </i>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, type PropType } from 'vue';\nimport { DS_VERIFICATION_STATES } from './DsVerificationIcon.types';\n\nconst props = defineProps({\n // true = verified · false = not verified · null/undefined = newly found.\n verified: {\n type: Boolean as PropType<boolean | null>,\n default: null,\n },\n // Emails (true) use the coral \"dangerous\" glyph when unverified; phones use orange.\n isContactData: {\n type: Boolean,\n default: false,\n },\n size: {\n type: Number,\n default: 20,\n },\n // Optional override for the accessible label / tooltip (the app owns i18n).\n // Empty → the English default derived from the verification state.\n label: {\n type: String,\n default: '',\n },\n});\n\nconst status = computed(() => {\n if (props.verified === null || props.verified === undefined) {\n return {\n state: DS_VERIFICATION_STATES.NEW,\n icon: 'new_releases',\n label: 'Newly found',\n };\n }\n if (props.verified) {\n return {\n state: DS_VERIFICATION_STATES.VERIFIED,\n icon: 'verified',\n label: 'Verified',\n };\n }\n return {\n state: DS_VERIFICATION_STATES.UNVERIFIED,\n icon: props.isContactData ? 'dangerous' : 'error',\n label: 'Not verified',\n };\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n\n.ds-verification-icon {\n line-height: 1;\n\n // States map DS colors (VerificationIcon.jsx). isContactData splits the\n // unverified color into coral (email) vs orange (phone).\n &--new {\n color: colors.$slate-soft; // --primary-secondary-text\n }\n &--verified {\n color: colors.$info-main;\n }\n &--unverified {\n color: colors.$warning-main; // phone (orange)\n\n &.ds-verification-icon--email {\n color: colors.$error-main; // email (coral \"dangerous\")\n }\n }\n}\n</style>\n","<template>\n <div class=\"ds-contact-value\" :class=\"{ 'ds-contact-value--main': main }\">\n <DsVerificationIcon :verified=\"verified\" :is-contact-data=\"isContactData\" />\n\n <span class=\"ds-contact-value__value\" :title=\"value\">{{ value }}</span>\n\n <!-- Marquee slot: badge and actions share one grid cell and cross-fade/slide\n on hover (or keyboard focus-within). -->\n <span class=\"ds-contact-value__marquee\">\n <!-- Sin aria-hidden: el tipo (Work/Mobile/…) es información real de clasificación\n del dato. El cross-fade del marquee es solo opacity, no lo saca del árbol de\n a11y; con el orden DOM value → badge, el SR lee \"ada@stripe.com, Work\". -->\n <DsBadge\n v-if=\"valueType\"\n class=\"ds-contact-value__badge\"\n :tone=\"main ? DS_BADGE_TONES.ACCENT : DS_BADGE_TONES.GREY\"\n :title=\"valueType\"\n >{{ valueType }}</DsBadge\n >\n\n <span class=\"ds-contact-value__actions\">\n <!-- Locked value: a static lock glyph replaces copy + rating entirely. -->\n <i\n v-if=\"hideValue\"\n class=\"material-icons ds-contact-value__lock\"\n :role=\"hideValueLabel ? 'img' : undefined\"\n :aria-label=\"hideValueLabel || undefined\"\n :aria-hidden=\"hideValueLabel ? undefined : 'true'\"\n >lock</i\n >\n\n <template v-else>\n <button\n type=\"button\"\n class=\"ds-contact-value__action\"\n :class=\"{ 'is-copied': copied }\"\n :aria-label=\"copied ? copiedLabel : copyLabel\"\n :title=\"copied ? copiedLabel : copyLabel\"\n @click=\"handleCopy\"\n >\n <i class=\"material-icons\" aria-hidden=\"true\">{{ copied ? 'check' : 'content_copy' }}</i>\n </button>\n\n <template v-if=\"showRate\">\n <button\n type=\"button\"\n class=\"ds-contact-value__action\"\n :aria-label=\"correctLabel\"\n :aria-pressed=\"correct === true\"\n :title=\"correctLabel\"\n @click=\"handleCorrect\"\n >\n <i\n :class=\"correct === true ? 'material-icons' : 'material-icons-outlined'\"\n aria-hidden=\"true\"\n >thumb_up</i\n >\n </button>\n <button\n type=\"button\"\n class=\"ds-contact-value__action\"\n :aria-label=\"incorrectLabel\"\n :aria-pressed=\"correct === false\"\n :title=\"incorrectLabel\"\n @click=\"handleIncorrect\"\n >\n <i\n :class=\"correct === false ? 'material-icons' : 'material-icons-outlined'\"\n aria-hidden=\"true\"\n >thumb_down</i\n >\n </button>\n </template>\n </template>\n </span>\n </span>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onBeforeUnmount, type PropType } from 'vue';\nimport { DsVerificationIcon } from '@shared/ui/DsVerificationIcon';\nimport { DsBadge, DS_BADGE_TONES } from '@shared/ui/DsBadge';\nimport { COPY, CORRECT, INCORRECT } from '@shared/constants/events';\n\nconst props = defineProps({\n value: { type: String, required: true },\n valueType: { type: String, default: '' },\n verified: { type: Boolean as PropType<boolean | null>, default: null },\n main: { type: Boolean, default: false },\n isContactData: { type: Boolean, default: false },\n showRate: { type: Boolean, default: false },\n correct: { type: Boolean as PropType<boolean | null>, default: null },\n // Locks the value: on hover a static lock glyph shows instead of copy/rate.\n hideValue: { type: Boolean, default: false },\n // User-facing labels (the app owns i18n); English DS defaults.\n copyLabel: { type: String, default: 'Copy' },\n copiedLabel: { type: String, default: 'Copied!' },\n correctLabel: { type: String, default: 'Correct' },\n incorrectLabel: { type: String, default: 'Incorrect' },\n // Accessible name for the lock glyph (empty → decorative/aria-hidden).\n hideValueLabel: { type: String, default: '' },\n});\n\n// Runtime array so the emit names are registered at runtime (the type-only\n// form with `typeof CONST` compiles to an empty `emits`, which lets listeners\n// fall through as native attrs). Names come from the shared events constants.\nconst emit = defineEmits([COPY, CORRECT, INCORRECT]);\n\nconst copied = ref(false);\nlet copiedTimer: ReturnType<typeof setTimeout> | undefined;\n\nfunction handleCopy() {\n if (typeof navigator !== 'undefined' && navigator.clipboard) {\n navigator.clipboard.writeText(props.value).catch(() => {});\n }\n copied.value = true;\n clearTimeout(copiedTimer);\n copiedTimer = setTimeout(() => {\n copied.value = false;\n }, 1200);\n emit(COPY, props.value);\n}\n\nfunction handleCorrect() {\n emit(CORRECT);\n}\nfunction handleIncorrect() {\n emit(INCORRECT);\n}\n\nonBeforeUnmount(() => clearTimeout(copiedTimer));\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n@use '../../assets/css/icons.scss' as i;\n\n.ds-contact-value {\n display: flex;\n align-items: center;\n\n padding: 6px; // DS ContactValue.jsx\n border-radius: r.$radius-control;\n\n background: transparent;\n transition: background-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n &:hover,\n &:focus-within {\n background-color: colors.$slate-bg; // --primary-background\n }\n\n &--main:hover,\n &--main:focus-within {\n background-color: colors.$accent-bg;\n }\n\n // -- value ---------------------------------------------------------------\n &__value {\n flex: 1;\n min-width: 0;\n margin: 0 8px; // DS\n\n color: colors.$slate-main; // --primary-main\n font-family: t.$font-family;\n font-size: t.font-size('body');\n\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n // -- marquee (badge + actions share one cell) ----------------------------\n &__marquee {\n display: inline-grid;\n align-items: center;\n justify-items: end;\n margin-left: auto;\n }\n\n &__badge,\n &__actions {\n grid-area: 1 / 1;\n transition:\n transform m.$duration-reveal m.$easing,\n opacity m.$duration-reveal m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n }\n\n // At rest: badge in place, actions parked to the right and hidden.\n &__badge {\n transform: translateX(0);\n opacity: 1;\n\n // Cap an arbitrarily long valueType (legacy getPhoneType can return a raw\n // company_name) so it can't squeeze the value span. `ch` is font-relative,\n // not a hardcoded px-scale value. The full label stays available via :title.\n max-width: 12ch;\n overflow: hidden;\n }\n &__actions {\n display: inline-flex;\n align-items: center;\n gap: 8px; // DS\n\n transform: translateX(8px);\n opacity: 0;\n pointer-events: none;\n }\n\n // On hover / keyboard focus: badge slides out, actions slide in.\n &:hover &__badge,\n &:focus-within &__badge {\n transform: translateX(8px);\n opacity: 0;\n }\n &:hover &__actions,\n &:focus-within &__actions {\n transform: translateX(0);\n opacity: 1;\n pointer-events: auto;\n }\n\n // -- action buttons ------------------------------------------------------\n &__action {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n padding: 0;\n border: none;\n background: none;\n cursor: pointer;\n\n color: colors.$slate-soft; // --primary-secondary-text\n\n .material-icons,\n .material-icons-outlined {\n font-size: i.icon-size(20);\n line-height: 1;\n }\n\n &.is-copied {\n color: colors.$success-shadow;\n }\n }\n\n // -- lock glyph (hideValue) ----------------------------------------------\n &__lock {\n color: colors.$slate-soft; // --primary-secondary-text (DS ContactValue.jsx)\n font-size: i.icon-size(20);\n line-height: 1;\n }\n}\n</style>\n","<template>\n <section class=\"ds-data-card\" :aria-label=\"title\" :aria-busy=\"loading ? 'true' : undefined\">\n <!-- header -->\n <div\n class=\"ds-data-card__header\"\n :class=\"{ 'is-collapsed': isCollapsed, 'is-collapsible': collapsible }\"\n @click=\"collapsible && toggle()\"\n >\n <div class=\"ds-data-card__heading\">\n <i class=\"material-icons ds-data-card__icon\" aria-hidden=\"true\">{{ icon }}</i>\n <span class=\"ds-data-card__title\">{{ title }}</span>\n </div>\n\n <div class=\"ds-data-card__actions\" @click.stop>\n <button\n v-if=\"!isCollapsed && showEdit\"\n type=\"button\"\n class=\"ds-data-card__edit\"\n @click=\"onEdit\"\n >\n {{ editLabel }}\n </button>\n\n <DsButton\n v-if=\"!isCollapsed && showSort\"\n variant=\"tertiary\"\n size=\"medium\"\n circle\n left-icon=\"swap_vert\"\n :aria-label=\"sortLabel\"\n @click=\"onSort\"\n />\n\n <DsButton\n v-if=\"collapsible\"\n variant=\"tertiary\"\n size=\"medium\"\n circle\n :left-icon=\"collapsed ? 'keyboard_arrow_down' : 'keyboard_arrow_up'\"\n :aria-label=\"toggleLabel\"\n @click=\"toggle\"\n />\n\n <button\n v-if=\"showClose\"\n type=\"button\"\n class=\"ds-data-card__close\"\n :aria-label=\"closeLabel\"\n @click=\"onClose\"\n >\n <i class=\"material-icons\" aria-hidden=\"true\">close</i>\n </button>\n </div>\n </div>\n\n <!-- body -->\n <div v-if=\"!isCollapsed\" class=\"ds-data-card__body\">\n <div v-if=\"loading\" class=\"ds-data-card__loading\">\n <slot name=\"loading\"><DsSpinner :label=\"loadingLabel || undefined\" /></slot>\n </div>\n\n <!-- Locked/upsell: the data is gated (plan/permission). Wins over empty and\n content (you can't know if gated data is empty), but loading still comes\n first. The #locked slot owns the upsell CTA; the default fallback mirrors\n the DS empty pattern (accent badge + bold title) so the state is never\n conveyed by icon alone. -->\n <div v-else-if=\"locked\" class=\"ds-data-card__locked\">\n <slot name=\"locked\">\n <span class=\"ds-data-card__state-badge ds-data-card__state-badge--accent\">\n <i class=\"material-icons ds-data-card__state-icon\" aria-hidden=\"true\">lock</i>\n </span>\n <div class=\"ds-data-card__state-text\">\n <span class=\"ds-data-card__state-title\">{{ lockedLabel }}</span>\n </div>\n </slot>\n </div>\n\n <!-- Empty: 1:1 with the DS DsDataCard empty state (DsDataCard.jsx) — a\n circular badge, a bold title, and an optional subtitle. -->\n <div v-else-if=\"empty\" class=\"ds-data-card__empty\">\n <span class=\"ds-data-card__state-badge\">\n <i class=\"material-icons ds-data-card__state-icon\" aria-hidden=\"true\">{{ icon }}</i>\n </span>\n <div class=\"ds-data-card__state-text\">\n <span class=\"ds-data-card__state-title\">{{ emptyLabel }}</span>\n <span v-if=\"emptySubtitle\" class=\"ds-data-card__state-subtitle\">{{ emptySubtitle }}</span>\n </div>\n </div>\n\n <template v-else>\n <div v-if=\"mostLikely\">\n <div v-if=\"showSections\" class=\"ds-data-card__section\">{{ mostLikelyLabel }}</div>\n <DsContactValue\n :value=\"mostLikely.value\"\n :value-type=\"mostLikely.type\"\n :verified=\"mostLikely.verified\"\n :correct=\"mostLikely.correct\"\n :hide-value=\"mostLikely.hideValue\"\n main\n :is-contact-data=\"isContactData\"\n :show-rate=\"showRate\"\n @copy=\"onCopy\"\n @correct=\"onCorrect(mostLikely)\"\n @incorrect=\"onIncorrect(mostLikely)\"\n />\n </div>\n\n <div v-if=\"more.length > 0\">\n <div v-if=\"showSections\" class=\"ds-data-card__section\">{{ moreLabel }}</div>\n <div class=\"ds-data-card__more\">\n <DsContactValue\n v-for=\"datum in more\"\n :key=\"datum.value\"\n :value=\"datum.value\"\n :value-type=\"datum.type\"\n :verified=\"datum.verified\"\n :correct=\"datum.correct\"\n :hide-value=\"datum.hideValue\"\n :is-contact-data=\"isContactData\"\n :show-rate=\"showRate\"\n @copy=\"onCopy\"\n @correct=\"onCorrect(datum)\"\n @incorrect=\"onIncorrect(datum)\"\n />\n </div>\n </div>\n </template>\n </div>\n </section>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, type PropType } from 'vue';\nimport { DsButton } from '@shared/ui/DsButton';\nimport { DsContactValue } from '@shared/ui/DsContactValue';\nimport { DsSpinner } from '@shared/ui/DsSpinner';\nimport { COPY, SORT, EDIT, CLOSE, CORRECT, INCORRECT } from '@shared/constants/events';\nimport type { DsContactDatum } from './DsDataCard.types';\n\nconst props = defineProps({\n title: { type: String, required: true },\n // Material Icon name for the header (e.g. \"email\", \"call\").\n icon: { type: String, default: 'email' },\n // Loading state: swaps the body for a centered spinner (wins over empty/data).\n // Override the spinner (e.g. an i18n'd label) via the #loading slot.\n loading: { type: Boolean, default: false },\n // Accessible name for the loading spinner (the app owns i18n); when omitted it\n // falls back to DsSpinner's own default. Overridable via the #loading slot.\n loadingLabel: { type: String, default: '' },\n // Locked/upsell state: the data is gated (plan/permission), so the body shows a\n // banner instead of the content (wins over empty/data; loading still wins over\n // it). Presentational only — the app decides when to gate and supplies the CTA\n // via the #locked slot. Additive/opt-in. NB: the header actions still render\n // while locked — turn them off yourself (showSort / showEdit) when gating.\n locked: { type: Boolean, default: false },\n mostLikely: { type: Object as PropType<DsContactDatum | null>, default: null },\n more: { type: Array as PropType<DsContactDatum[]>, default: () => [] },\n collapsible: { type: Boolean, default: false },\n showSort: { type: Boolean, default: true },\n showEdit: { type: Boolean, default: false },\n showClose: { type: Boolean, default: false },\n isContactData: { type: Boolean, default: false },\n showRate: { type: Boolean, default: false },\n // User-facing labels (the app owns i18n); English DS defaults.\n editLabel: { type: String, default: 'Edit' },\n emptyLabel: { type: String, default: 'No data found' },\n // Optional muted line under the empty title (DS DsDataCard `emptySubtitle`),\n // e.g. \"We couldn't find any phone numbers for this contact.\"\n emptySubtitle: { type: String, default: '' },\n // Fallback text for the locked banner when no #locked slot is provided.\n lockedLabel: { type: String, default: 'Locked' },\n mostLikelyLabel: { type: String, default: 'MOST LIKELY' },\n moreLabel: { type: String, default: 'MORE' },\n sortLabel: { type: String, default: 'Sort' },\n toggleLabel: { type: String, default: 'Toggle' },\n closeLabel: { type: String, default: 'Close' },\n});\n\n// Runtime array so the emit names are registered at runtime (the type-only\n// form with `typeof CONST` compiles to an empty `emits`, which lets listeners\n// fall through as native attrs). Names come from the shared events constants.\nconst emit = defineEmits([COPY, SORT, EDIT, CLOSE, CORRECT, INCORRECT]);\n\nconst collapsed = ref(false);\n\nconst total = computed(() => props.more.length + (props.mostLikely ? 1 : 0));\nconst showSections = computed(() => total.value > 3);\nconst empty = computed(() => total.value === 0);\nconst isCollapsed = computed(() => props.collapsible && collapsed.value);\n\nfunction toggle() {\n collapsed.value = !collapsed.value;\n}\nfunction onCopy(value: string) {\n emit(COPY, value);\n}\nfunction onSort() {\n emit(SORT);\n}\nfunction onEdit() {\n emit(EDIT);\n}\nfunction onClose() {\n emit(CLOSE);\n}\nfunction onCorrect(datum: DsContactDatum) {\n emit(CORRECT, datum);\n}\nfunction onIncorrect(datum: DsContactDatum) {\n emit(INCORRECT, datum);\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/shadow.scss' as sh;\n@use '../../assets/css/icons.scss' as i;\n@use '../../assets/css/spacing.scss' as s;\n\n.ds-data-card {\n display: block;\n\n padding: 8px;\n\n background-color: colors.$white;\n border-radius: r.$radius-card;\n box-shadow: sh.$shadow-card;\n\n // -- header --------------------------------------------------------------\n &__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n padding: 6px 6px 14px; // DS DataCard.jsx\n border-bottom: 1px solid colors.$slate-tint; // --primary-tint\n\n &.is-collapsible {\n cursor: pointer;\n }\n &.is-collapsed {\n border-bottom: none;\n }\n }\n\n &__heading {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n }\n\n &__icon {\n font-size: i.icon-size(20);\n line-height: 1;\n color: colors.$slate-main; // --primary-main\n }\n\n &__title {\n font-family: t.$font-family;\n font-weight: t.$weight-bold;\n font-size: t.font-size('body');\n color: colors.$slate-main;\n\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n &__actions {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n &__edit {\n padding: 0;\n border: none;\n background: none;\n cursor: pointer;\n\n font-family: t.$font-family;\n font-weight: t.$weight-bold;\n font-size: t.font-size('caption');\n color: colors.$accent-main;\n }\n\n &__close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n padding: 0;\n border: none;\n background: none;\n cursor: pointer;\n\n color: colors.$slate-soft; // --primary-secondary-text\n\n .material-icons {\n font-size: i.icon-size(20);\n line-height: 1;\n }\n }\n\n // -- body ----------------------------------------------------------------\n &__body {\n padding-top: 8px;\n }\n\n &__section {\n padding: 6px;\n\n font-family: t.$font-family;\n font-size: t.font-size('micro');\n line-height: 18px;\n letter-spacing: 0.04em;\n color: colors.$slate-main;\n }\n\n &__more {\n display: flex;\n flex-direction: column;\n gap: 8px;\n\n max-height: 132px; // DS: ~3 rows before the \"MORE\" list scrolls\n overflow: auto;\n }\n\n // Loading: center the spinner in the body area.\n &__loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: s.spacing(3); // 24\n }\n\n // Empty + locked share the DS DsDataCard \"state\" pattern (DsDataCard.jsx): a\n // centered circular badge, a bold title, and an optional subtitle. Locked tints\n // the badge with the accent to read as an upsell; its #locked slot can replace\n // the whole fallback with a CTA.\n &__empty,\n &__locked {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: s.spacing(1.5); // DS gap: 12\n\n padding: s.spacing(3) s.spacing(2.5); // DS padding: 24px 20px\n\n text-align: center;\n }\n\n &__state-badge {\n display: flex;\n align-items: center;\n justify-content: center;\n\n width: 44px; // DS: 44px badge\n height: 44px;\n\n background-color: colors.$slate-bg; // DS --primary-background\n border-radius: 50%;\n\n &--accent {\n background-color: colors.$accent-bg;\n }\n }\n\n &__state-icon {\n font-size: i.icon-size(22); // DS DsDataCard.jsx: 22px glyph\n line-height: 1;\n color: colors.$slate-soft; // DS --primary-secondary-text\n\n .ds-data-card__state-badge--accent & {\n color: colors.$accent-main;\n }\n }\n\n &__state-text {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: s.spacing(0.5); // DS gap: 3\n }\n\n &__state-title {\n font-family: t.$font-family;\n font-weight: t.$weight-bold;\n font-size: t.font-size('body');\n color: colors.$slate-main;\n }\n\n &__state-subtitle {\n font-family: t.$font-family;\n font-size: t.font-size('caption');\n line-height: 18px;\n color: colors.$slate-soft;\n }\n}\n</style>\n","<template>\n <label class=\"ds-radio\" :class=\"{ 'is-disabled': disabled }\">\n <input\n type=\"radio\"\n class=\"ds-radio__native\"\n :name=\"name || undefined\"\n :value=\"value || undefined\"\n :checked=\"model\"\n :disabled=\"disabled\"\n @change=\"handleChange\"\n />\n <span class=\"ds-radio__dot\" :class=\"{ 'is-active': model }\" aria-hidden=\"true\"></span>\n <span v-if=\"$slots.default || label\" class=\"ds-radio__label\"\n ><slot>{{ label }}</slot></span\n >\n </label>\n</template>\n\n<script setup lang=\"ts\">\nconst model = defineModel<boolean>({ default: false });\n\nconst props = defineProps({\n disabled: Boolean,\n // Label text (DS `label` prop); a default slot overrides it.\n label: { type: String, default: '' },\n name: { type: String, default: '' },\n value: { type: String, default: '' },\n});\n\n// Native radios never toggle themselves off — the browser only fires\n// `change` when THIS radio becomes checked (click, or keyboard arrow-nav\n// within a shared `name` group). Consumers own exclusivity across a group\n// by driving each instance's `model-value` off one selected value.\nfunction handleChange() {\n if (props.disabled) return;\n model.value = true;\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/spacing.scss' as s;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n\n.ds-radio {\n display: inline-flex;\n align-items: center;\n gap: s.spacing(1);\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n\n cursor: pointer;\n user-select: none;\n\n &.is-disabled {\n cursor: not-allowed;\n opacity: 0.6; // DS DsRadio.jsx: opacity 0.6 when disabled\n }\n\n /* Native control drives state + focus, visually replaced by the custom dot. */\n &__native {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n }\n\n &__dot {\n box-sizing: border-box;\n flex: none;\n\n width: 16px;\n height: 16px;\n\n background-color: colors.$white;\n\n border: 2px solid colors.$slate-main;\n border-radius: r.$radius-full;\n\n transition: border m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n // Thick accent border on a fixed-size, border-box circle creates the\n // filled-dot illusion (DS DsRadio.jsx: 5px solid accent-main).\n &.is-active {\n border-width: 5px;\n border-color: colors.$accent-main;\n }\n }\n\n // Hover affordance on the unchecked dot: preview the accent so the control\n // reads as selectable (skipped when disabled or already checked).\n &:hover:not(.is-disabled) .ds-radio__dot:not(.is-active) {\n border-color: colors.$accent-main;\n }\n\n /* Focus ring on the dot when the (hidden) native input is focused. */\n &__native:focus-visible + &__dot {\n outline: 2px solid colors.$accent-mid;\n outline-offset: 1px;\n }\n\n &__label {\n line-height: 1.3;\n }\n}\n</style>\n","<template>\n <button\n type=\"button\"\n role=\"switch\"\n class=\"ds-switch\"\n :class=\"{ 'is-disabled': disabled, 'is-loading': loading }\"\n :aria-checked=\"model\"\n :aria-busy=\"loading || undefined\"\n :aria-disabled=\"disabled || loading || undefined\"\n :aria-label=\"ariaLabel || undefined\"\n :disabled=\"disabled || loading\"\n @click=\"toggle\"\n >\n <span class=\"ds-switch__thumb\" aria-hidden=\"true\">\n <!-- Busy indicator lives INSIDE the thumb so it rides the same translateX as\n the knob and stays aligned to the on/off position. Decorative (thumb is\n aria-hidden): the button's aria-busy is the semantic signal for AT. -->\n <DsSpinner\n v-if=\"loading\"\n class=\"ds-switch__spinner\"\n :size=\"12\"\n :tone=\"DS_SPINNER_TONES.DARK\"\n label=\"\"\n />\n </span>\n </button>\n</template>\n\n<script setup lang=\"ts\">\nimport { DsSpinner, DS_SPINNER_TONES } from '@shared/ui/DsSpinner';\n\nconst model = defineModel<boolean>({ default: false });\n\nconst props = defineProps({\n disabled: Boolean,\n // Async busy state for a toggle whose commit is pending (e.g. a server round-trip\n // that may revert). Blocks interaction and shows a spinner in the thumb while it\n // resolves. Presentational-only: the app owns the optimistic value + revert; the\n // component just reflects the `loading` and `modelValue` it's handed.\n loading: Boolean,\n // Accessible name for the switch (a control with no visible text). See\n // DsSwitch.types.ts for why this stays optional rather than a required prop.\n ariaLabel: { type: String, default: '' },\n});\n\n// Native <button> gives Enter/Space activation for free; this only handles\n// the resulting click (mouse or keyboard-triggered). Loading blocks the toggle\n// the same way disabled does — the pending op must resolve first.\nfunction toggle() {\n if (props.disabled || props.loading) return;\n model.value = !model.value;\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/shadow.scss' as sh;\n@use '../../assets/css/motion.scss' as m;\n\n.ds-switch {\n display: inline-flex;\n align-items: center;\n flex: none;\n\n width: 40px;\n height: 20px;\n padding: 2px;\n\n background-color: colors.$grey-3;\n border: none;\n border-radius: r.$radius-full;\n outline: none;\n\n cursor: pointer;\n transition: background-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n &:focus-visible {\n outline: 2px solid colors.$accent-mid;\n outline-offset: 1px;\n }\n\n &[aria-checked='true'] {\n background-color: colors.$accent-main;\n }\n\n &.is-disabled {\n cursor: not-allowed;\n opacity: 0.6; // DS DsSwitch.jsx: opacity 0.6 when disabled\n }\n\n // Busy ≠ disabled: keep the track color at full opacity so the on/off state\n // stays readable while the async op resolves — only the spinner signals \"pending\".\n &.is-loading {\n cursor: progress;\n }\n\n &__thumb {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n\n width: 16px;\n height: 16px;\n\n background-color: colors.$white;\n border-radius: r.$radius-full;\n box-shadow: sh.$shadow-thumb;\n\n transition: transform m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n }\n\n // Track inner width (40 - 2*2 padding) minus the thumb (16) = 20px travel.\n &[aria-checked='true'] &__thumb {\n transform: translateX(20px);\n }\n\n // The spinner sits centered in the white thumb; the dark tone reads against it\n // in both the on (accent track) and off (grey track) positions.\n &__spinner {\n flex: none;\n }\n}\n</style>\n","<template>\n <button\n type=\"button\"\n class=\"ds-item-selector-button\"\n :class=\"{ 'is-empty': !item, 'is-disabled': disabled }\"\n aria-haspopup=\"listbox\"\n :aria-label=\"ariaLabel || undefined\"\n :disabled=\"disabled\"\n @click=\"handleClick\"\n >\n <span class=\"ds-item-selector-button__label\">{{ item ? item.name : emptyTitle }}</span>\n <i\n v-if=\"icon\"\n class=\"ds-item-selector-button__icon\"\n :class=\"item ? 'material-icons' : 'material-icons-outlined'\"\n :style=\"item?.color ? { color: item.color } : undefined\"\n aria-hidden=\"true\"\n >{{ icon }}</i\n >\n </button>\n</template>\n\n<script setup lang=\"ts\">\nimport { type PropType } from 'vue';\nimport { CLICK } from '@shared/constants/events';\nimport type { DsSelectorItem } from './DsItemSelectorButton.types';\n\nconst props = defineProps({\n item: { type: Object as PropType<DsSelectorItem | null>, default: null },\n // Material Icons ligature (e.g. `folder`); omitted entirely when unset.\n icon: { type: String, default: '' },\n emptyTitle: { type: String, default: '' },\n // Accessible name (see .types.ts). Required in the empty state when there's no\n // `emptyTitle`; the visible text (item.name / emptyTitle) names it otherwise.\n ariaLabel: { type: String, default: '' },\n disabled: Boolean,\n});\n\nconst emit = defineEmits([CLICK]);\n\nfunction handleClick(event: MouseEvent) {\n if (props.disabled) return;\n emit(CLICK, event);\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/spacing.scss' as s;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/icons.scss' as i;\n@use '../../assets/css/motion.scss' as m;\n\n.ds-item-selector-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px; // DS ItemSelectorButton.jsx literal gap (not on the 8px scale)\n\n height: s.control-height('medium');\n padding: 0 s.control-padding('medium');\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n\n background-color: colors.$white;\n\n border: 1px solid colors.$slate-tint;\n border-radius: r.$radius-full;\n outline: none;\n\n cursor: pointer;\n transition: border-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n &:hover:not(.is-disabled) {\n border-color: colors.$slate-mid;\n }\n\n &:focus-visible {\n outline: 2px solid colors.$accent-mid;\n outline-offset: 1px;\n }\n\n &.is-empty {\n color: colors.$slate-mid;\n }\n\n &.is-disabled {\n cursor: not-allowed;\n opacity: 0.6;\n }\n\n &__label {\n max-width: 200px;\n overflow: hidden;\n\n font-weight: t.$weight-bold; // DS: bold regardless of selected/empty\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &__icon {\n flex: none;\n font-size: i.icon-size(16);\n line-height: i.icon-size(16);\n }\n}\n</style>\n","// DsSelect — dropdown select. Visual + interaction port of the DS `Select.jsx`\n// (`GenericSelect`, two visual modes: grey pill \"default\" and the Prospector\n// accent filter chip \"filterSummary\"). The DS source renders a bare\n// `<div onClick>` with zero semantics; this library version keeps the exact\n// same visual API (options / placeholder / multiple / selectType /\n// showCheckbox / width) but adds a real interaction model — native <button>\n// trigger, listbox/option roles, full keyboard support, and focus management\n// — per the WAI-ARIA APG \"Collapsible Dropdown Listbox\" (select-only combobox)\n// pattern. See DsSelect.vue for the implementation notes.\n\nexport interface DsSelectOption {\n value: string | number;\n label: string;\n subLabel?: string;\n /** Material Icons ligature name. */\n icon?: string;\n /** Icon color (any CSS color value) — mirrors the DS `option.color` pass-through. */\n color?: string;\n /**\n * Optional group name (optgroup-style). Consecutive options that share a\n * `group` are wrapped in a real ARIA group (`role=\"group\"` labelled by a\n * non-selectable header, with a divider); options without a `group` render\n * ungrouped. Grouping follows the order of `options` (like native\n * `<optgroup>`) — the component does not re-sort. Keyboard navigation skips\n * the headers (they are not selectable options).\n */\n group?: string;\n}\n\n// selectType: 'default' — grey pill · 'filterSummary' — accent filter chip\n// (Prospector-style: grey until something is selected, then flips to accent).\nexport const DS_SELECT_TYPES = {\n DEFAULT: 'default',\n FILTER_SUMMARY: 'filterSummary',\n} as const;\nexport type DsSelectType = (typeof DS_SELECT_TYPES)[keyof typeof DS_SELECT_TYPES];\n\n// width — defaults to 'auto' (shrink-to-content, the historical behaviour, so\n// the feature stays non-breaking). Opt into a named step for a STABLE width that\n// no longer resizes with the selected label; 'full' fills the container (like\n// DsInput). Any number (→px) or CSS string (e.g. '18rem') works for a custom width.\nexport const DS_SELECT_WIDTHS = {\n AUTO: 'auto', // shrink to content — resizes with the selected label (default)\n SMALL: 'small', // 128px\n MEDIUM: 'medium', // 200px\n LARGE: 'large', // 280px\n FULL: 'full', // 100% of the container\n} as const;\nexport type DsSelectWidthName = (typeof DS_SELECT_WIDTHS)[keyof typeof DS_SELECT_WIDTHS];\n// The `width` prop accepts a named step above, or a custom width: a number\n// (rendered as px) or any CSS width string.\nexport type DsSelectWidth = DsSelectWidthName | number | string;\n\n// v-model shape — DECIDED (idiomatic Vue, NOT the DS \"value is always an\n// array\" convention):\n// multiple = false → string | number | null (scalar, like a native <select>)\n// multiple = true → (string | number)[] (array of selected values)\n// One generic type covers both; DsSelect.vue narrows it internally based on\n// the `multiple` prop instead of exposing two separate component variants.\nexport type DsSelectModelValue = string | number | (string | number)[] | null;\n","<template>\n <div\n ref=\"rootRef\"\n class=\"ds-select\"\n :class=\"[`ds-select--${selectType}`, widthClass, { 'is-open': open }]\"\n :style=\"rootStyle\"\n >\n <div\n class=\"ds-select__control\"\n :class=\"{ 'is-disabled': disabled, 'is-filter-selected': isFilterSelected }\"\n >\n <button\n ref=\"triggerRef\"\n type=\"button\"\n class=\"ds-select__trigger\"\n :disabled=\"disabled\"\n aria-haspopup=\"listbox\"\n :aria-expanded=\"open\"\n :aria-controls=\"open ? listboxId : undefined\"\n :aria-activedescendant=\"filterable ? undefined : activeOptionId\"\n @click=\"toggleOpen\"\n @keydown=\"onTriggerKeydown\"\n >\n <span class=\"ds-select__summary\" :title=\"summaryText\">{{ summaryText }}</span>\n </button>\n\n <button\n v-if=\"isFilter && hasSelection\"\n type=\"button\"\n class=\"ds-select__clear\"\n :disabled=\"disabled\"\n :aria-label=\"clearLabel || undefined\"\n @click=\"clearSelection\"\n >\n <i class=\"material-icons\" aria-hidden=\"true\">close</i>\n </button>\n <i v-else class=\"material-icons ds-select__chevron\" aria-hidden=\"true\">keyboard_arrow_down</i>\n </div>\n\n <div v-if=\"open\" class=\"ds-select__popup\">\n <!-- Type-to-filter (opt-in via `filterable`). The search field is a SIBLING\n of the listbox, never a child: an <input> inside role=\"listbox\" is\n invalid ARIA. It carries the editable-combobox roles/state and owns\n aria-activedescendant while open (focus lives here, not on the trigger). -->\n <div v-if=\"filterable\" class=\"ds-select__search\">\n <i class=\"material-icons ds-select__search-icon\" aria-hidden=\"true\">search</i>\n <input\n ref=\"searchRef\"\n type=\"text\"\n class=\"ds-select__search-input\"\n role=\"combobox\"\n aria-expanded=\"true\"\n :aria-controls=\"listboxId\"\n :aria-activedescendant=\"activeOptionId\"\n aria-autocomplete=\"list\"\n autocomplete=\"off\"\n :aria-label=\"searchLabel\"\n :placeholder=\"searchPlaceholder\"\n :value=\"query\"\n @input=\"onSearchInput\"\n @keydown=\"onSearchKeydown\"\n />\n </div>\n\n <!-- @mousedown.prevent keeps focus on the trigger/search input when an\n option is clicked (see the option's @click.prevent for the other half). -->\n <ul\n :id=\"listboxId\"\n class=\"ds-select__listbox\"\n role=\"listbox\"\n :aria-multiselectable=\"multiple || undefined\"\n :aria-busy=\"loading || undefined\"\n @mousedown.prevent\n >\n <!-- Loading takes precedence over both options and the empty state\n (async fetch in flight). aria-busy on the listbox + a #loading slot\n for the app to override the default DsSpinner. -->\n <li v-if=\"loading\" class=\"ds-select__loading\" role=\"presentation\">\n <slot name=\"loading\"><DsSpinner size=\"small\" /></slot>\n </li>\n\n <template v-else>\n <!-- One <li> per run. A grouped run is a REAL ARIA group (role=\"group\"\n named by its header). An ungrouped run is a transparent\n role=\"presentation\" wrapper, so its options stay owned by the\n listbox. Options carry role=\"option\" regardless of nesting; the flat\n `it.index` keeps aria-activedescendant / keyboard nav intact. -->\n <li\n v-for=\"run in runs\"\n :key=\"run.key\"\n :role=\"run.kind === 'group' ? 'group' : 'presentation'\"\n :aria-labelledby=\"run.kind === 'group' ? run.headerId : undefined\"\n :class=\"run.kind === 'group' ? 'ds-select__group' : 'ds-select__run'\"\n >\n <span v-if=\"run.kind === 'group'\" :id=\"run.headerId\" class=\"ds-select__group-header\">{{\n run.label\n }}</span>\n\n <!-- @click.prevent is load-bearing: if a consumer wraps DsSelect in a\n <label> (common for form fields), the label's activation behaviour\n re-fires this click onto the labelable trigger <button>, toggling the\n dropdown (multi would close, single would re-open). Cancelling the\n option click's default suppresses that re-fire. -->\n <div\n v-for=\"it in run.items\"\n :id=\"optionId(it.index)\"\n :key=\"it.option.value\"\n class=\"ds-select__option\"\n :class=\"{\n 'is-selected': isSelected(it.option.value),\n 'is-active': it.index === activeIndex,\n 'ds-select__option--checkbox': showCheckbox,\n }\"\n role=\"option\"\n :aria-selected=\"isSelected(it.option.value)\"\n @click.prevent=\"selectOption(it.option.value)\"\n @mouseenter=\"activeIndex = it.index\"\n >\n <DsCheckbox\n v-if=\"showCheckbox\"\n class=\"ds-select__option-check\"\n :model-value=\"isSelected(it.option.value)\"\n disabled\n aria-hidden=\"true\"\n />\n <!-- #option scoped slot (opt-in): custom per-option content (avatar,\n image, arbitrary markup). Falls back to the icon/label\n key-mapping. The decorative checkbox glyph stays OUTSIDE the slot\n so showCheckbox keeps working with custom content. -->\n <slot\n name=\"option\"\n :option=\"it.option\"\n :selected=\"isSelected(it.option.value)\"\n :active=\"it.index === activeIndex\"\n >\n <i\n v-if=\"it.option.icon\"\n class=\"material-icons ds-select__option-icon\"\n :style=\"it.option.color ? { color: it.option.color } : undefined\"\n aria-hidden=\"true\"\n >{{ it.option.icon }}</i\n >\n <span class=\"ds-select__option-label\"\n >{{ it.option.label\n }}<span v-if=\"it.option.subLabel\" class=\"ds-select__option-sublabel\"\n >, {{ it.option.subLabel }}</span\n ></span\n >\n </slot>\n </div>\n </li>\n\n <!-- Empty state: no options, or a filtered list with no matches.\n role=presentation so keyboard nav / moveActive ignore it\n (moveActive already no-ops on len===0). -->\n <li v-if=\"visibleOptions.length === 0\" class=\"ds-select__empty\" role=\"presentation\">\n <slot name=\"empty\">{{ emptyText }}</slot>\n </li>\n </template>\n </ul>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n reactive,\n ref,\n useId,\n watch,\n type PropType,\n} from 'vue';\nimport { DsCheckbox } from '@shared/ui/DsCheckbox';\nimport { DsSpinner } from '@shared/ui/DsSpinner';\nimport { SEARCH } from '@shared/constants/events';\nimport {\n DS_SELECT_TYPES,\n DS_SELECT_WIDTHS,\n type DsSelectOption,\n type DsSelectType,\n type DsSelectWidth,\n type DsSelectModelValue,\n} from './DsSelect.types';\n\nconst props = defineProps({\n options: { type: Array as PropType<DsSelectOption[]>, required: true },\n placeholder: { type: String, default: 'Select an option' },\n multiple: Boolean,\n selectType: { type: String as PropType<DsSelectType>, default: DS_SELECT_TYPES.DEFAULT },\n showCheckbox: Boolean,\n // Named step ('small'|'medium'|'large'|'full'|'auto') for a STABLE width, or a\n // custom width (number → px, or any CSS string). Defaults to 'auto'\n // (shrink-to-content — the historical behaviour, kept so this stays a\n // non-breaking, opt-in feature); pass a fixed step for a width that doesn't\n // resize with the selected label. See DsSelect.types.ts `DS_SELECT_WIDTHS`.\n width: {\n type: [Number, String] as PropType<DsSelectWidth>,\n default: DS_SELECT_WIDTHS.AUTO,\n },\n disabled: Boolean,\n // Opt-in type-to-filter: renders a search input inside the open dropdown.\n // Off by default, so existing usages keep the plain select-only behaviour.\n filterable: Boolean,\n // Remote filtering: skip the built-in local filter and let the app own it by\n // replacing `options` in response to the `search` event. Prevents a double\n // filter (local re-filtering of already server-filtered results). No effect\n // unless `filterable` is also set.\n remote: Boolean,\n // Async loading state: while true the listbox shows a spinner + aria-busy\n // instead of options/empty. A #loading slot overrides the default DsSpinner.\n loading: Boolean,\n // Accessible name for the filter search input (a placeholder is not a reliable\n // accessible name). English default; the app owns i18n.\n searchLabel: { type: String, default: 'Filter options' },\n // Visible placeholder for the filter search input.\n searchPlaceholder: { type: String, default: 'Search' },\n // Accessible name for the filterSummary clear (×) control. The app can override\n // the wording (e.g. localized), but we default to a non-empty fallback so the\n // icon-only button is never left without an accessible name for screen readers.\n clearLabel: { type: String, default: 'Clear selection' },\n // Shown when the visible list is empty (no options, or a filtered list with no\n // matches). English default (the app owns i18n); a #empty slot overrides it.\n emptyText: { type: String, default: 'No options' },\n});\n\n// `search` carries the typed filter text. update:modelValue is declared\n// implicitly by defineModel below.\nconst emit = defineEmits([SEARCH]);\n\n// v-model — see DsSelect.types.ts `DsSelectModelValue` for the full rationale:\n// !multiple → scalar `string | number | null` (like a native <select>)\n// multiple → `(string | number)[]`\n// This is a deliberate departure from the DS source (which always hands back\n// an array via onChange, even for a single value) in favour of the idiomatic\n// Vue shape a consumer actually expects from v-model.\nconst model = defineModel<DsSelectModelValue>({ default: null });\n\n// useId() gives a per-instance, SSR-safe unique id. A plain `let uid = 0`\n// here would reset per instance (it runs inside setup), so every DsSelect\n// would collide on `ds-select-1-*` and produce duplicate listbox/option ids.\nconst instanceId = `ds-select-${useId()}`;\nconst listboxId = `${instanceId}-listbox`;\nconst optionId = (index: number) => `${instanceId}-option-${index}`;\n\nconst rootRef = ref<HTMLElement | null>(null);\nconst triggerRef = ref<HTMLElement | null>(null);\nconst searchRef = ref<HTMLInputElement | null>(null);\n\nconst open = ref(false);\nconst activeIndex = ref(-1);\nconst query = ref('');\n\nconst isFilter = computed(() => props.selectType === DS_SELECT_TYPES.FILTER_SUMMARY);\n\n// Local filtering runs only when filterable AND not remote. In remote mode the\n// app replaces `options` itself (via the `search` event), so filtering here\n// would double-filter its already-filtered results.\nconst localFilterActive = computed(() => props.filterable && !props.remote);\n\nconst filteredOptions = computed<DsSelectOption[]>(() => {\n if (!localFilterActive.value) return props.options;\n const q = query.value.trim().toLowerCase();\n if (!q) return props.options;\n return props.options.filter(\n (o) =>\n o.label.toLowerCase().includes(q) ||\n (o.subLabel ? o.subLabel.toLowerCase().includes(q) : false)\n );\n});\n\n// The list actually rendered and navigated. While loading, nothing is\n// navigable (the spinner replaces the rows), so keyboard nav / active-descendant\n// stay consistent with what's on screen.\nconst visibleOptions = computed<DsSelectOption[]>(() =>\n props.loading ? [] : filteredOptions.value\n);\n\n// Render is grouped into runs so grouped options are exposed as REAL ARIA groups\n// (role=\"group\" named by their header) — not just visually. Each option keeps its\n// flat index into `visibleOptions`, so keyboard nav / activeIndex / optionId /\n// aria-activedescendant are unchanged and headers are never navigable (they are\n// not in visibleOptions). Consecutive options sharing a `group` form one group\n// run; ungrouped options form a transparent \"plain\" run (its options stay owned\n// by the listbox, since role=\"presentation\" is transparent to assistive tech).\ntype OptionItem = { option: DsSelectOption; index: number };\ntype Run =\n | { kind: 'group'; label: string; headerId: string; key: string; items: OptionItem[] }\n | { kind: 'plain'; key: string; items: OptionItem[] };\n\nconst runs = computed<Run[]>(() => {\n const result: Run[] = [];\n visibleOptions.value.forEach((option, index) => {\n const g = option.group;\n const last = result[result.length - 1];\n if (g) {\n if (last?.kind === 'group' && last.label === g) last.items.push({ option, index });\n else\n result.push({\n kind: 'group',\n label: g,\n headerId: `${instanceId}-group-${index}`,\n key: `g-${index}`,\n items: [{ option, index }],\n });\n } else if (last?.kind === 'plain') {\n last.items.push({ option, index });\n } else {\n result.push({ kind: 'plain', key: `p-${index}`, items: [{ option, index }] });\n }\n });\n return result;\n});\n\nconst selectedValues = computed<(string | number)[]>(() => {\n if (props.multiple) return Array.isArray(model.value) ? model.value : [];\n return model.value !== null && model.value !== undefined ? [model.value as string | number] : [];\n});\nconst hasSelection = computed(() => selectedValues.value.length > 0);\nconst isFilterSelected = computed(() => isFilter.value && hasSelection.value);\n\nfunction isSelected(value: string | number) {\n return selectedValues.value.includes(value);\n}\n\n// Cache of last-known value → label, kept fresh from `options` (deep + immediate\n// so it tracks in-place label edits and push/splice on the same array, not only\n// array-reference swaps). It is ONLY a fallback: labelFor() reads the CURRENT\n// options first — so an in-place label change is reflected reactively — and uses\n// the cache only when the selected value is absent from the current options,\n// which is what `remote` filtering causes when it replaces `options` with a\n// subset. Without the fallback the trigger would degrade to the raw value\n// (e.g. \"ba\" instead of \"Buenos Aires\").\nconst labelCache = reactive(new Map<string | number, string>());\nwatch(\n () => props.options,\n (opts) => opts.forEach((o) => labelCache.set(o.value, o.label)),\n { immediate: true, deep: true }\n);\n\nfunction labelFor(value: string | number) {\n return (\n props.options.find((o) => o.value === value)?.label ?? labelCache.get(value) ?? String(value)\n );\n}\n\nconst summaryText = computed(() => {\n if (isFilter.value) {\n if (hasSelection.value) {\n return props.multiple\n ? `${props.placeholder} (${selectedValues.value.length})`\n : labelFor(selectedValues.value[0]);\n }\n return props.placeholder;\n }\n if (hasSelection.value) {\n return props.multiple\n ? selectedValues.value.map(labelFor).join(', ')\n : labelFor(selectedValues.value[0]);\n }\n return props.placeholder;\n});\n\n// A named width step is applied via a modifier class (so the fixed value comes\n// from the SCSS token); a custom width (number/other string) goes inline.\nconst NAMED_WIDTHS = Object.values(DS_SELECT_WIDTHS) as string[];\nconst widthClass = computed(() =>\n typeof props.width === 'string' && NAMED_WIDTHS.includes(props.width)\n ? `ds-select--w-${props.width}`\n : null\n);\nconst rootStyle = computed(() => {\n if (widthClass.value) return undefined; // handled by the modifier class\n return { width: typeof props.width === 'number' ? `${props.width}px` : props.width };\n});\n\nconst activeOptionId = computed(() =>\n open.value && activeIndex.value >= 0 ? optionId(activeIndex.value) : undefined\n);\n\nfunction findInitialActiveIndex(): number {\n if (visibleOptions.value.length === 0) return -1;\n const firstSelected = visibleOptions.value.findIndex((o) => isSelected(o.value));\n return firstSelected >= 0 ? firstSelected : 0;\n}\n\nfunction openDropdown() {\n if (props.disabled) return;\n open.value = true;\n activeIndex.value = findInitialActiveIndex();\n // Focus goes to the search input when filterable, so the user can type\n // immediately; aria-activedescendant then rides on the input, not the trigger.\n if (props.filterable) nextTick(() => searchRef.value?.focus());\n}\n\n// `refocusTrigger` is false for outside-click / Tab-away closes, where focus is\n// already moving elsewhere and shouldn't be yanked back to the trigger.\nfunction closeDropdown(refocusTrigger = true) {\n open.value = false;\n activeIndex.value = -1;\n // Reset the filter so reopening starts fresh. Assigned after open=false so the\n // visibleOptions watcher (guarded on open) doesn't reset activeIndex here.\n // Emit search('') on the reset too: in `remote` mode the parent owns `options`\n // and otherwise can't know the query was cleared, so it would keep serving the\n // last narrowed result set on reopen (empty input, stale list). Local mode\n // already recomputes the full list from the empty query — the emit is harmless\n // there. Guarded on a non-empty query so a plain open/close doesn't emit.\n if (query.value) {\n query.value = '';\n emit(SEARCH, '');\n }\n if (refocusTrigger) nextTick(() => triggerRef.value?.focus());\n}\n\nfunction toggleOpen() {\n if (props.disabled) return;\n if (open.value) closeDropdown();\n else openDropdown();\n}\n\nfunction selectOption(value: string | number) {\n if (props.disabled) return;\n if (props.multiple) {\n const current = selectedValues.value;\n model.value = current.includes(value)\n ? current.filter((v) => v !== value)\n : [...current, value];\n // Multi-select keeps the dropdown open so users can toggle several options.\n return;\n }\n // DS parity (Select.jsx toggleValue): single-select + showCheckbox lets\n // clicking the already-selected option deselect it.\n const alreadySelected = isSelected(value);\n model.value = alreadySelected && props.showCheckbox ? null : value;\n closeDropdown();\n}\n\nfunction clearSelection() {\n if (props.disabled) return;\n model.value = props.multiple ? [] : null;\n}\n\nfunction moveActive(delta: number) {\n const len = visibleOptions.value.length;\n if (len === 0) return;\n if (activeIndex.value < 0) {\n activeIndex.value = delta > 0 ? 0 : len - 1;\n } else {\n activeIndex.value = (activeIndex.value + delta + len) % len;\n }\n nextTick(() => {\n // Optional-chain the call itself, not just the element: scrollIntoView is\n // unreliable/absent in some DOM test environments (jsdom).\n document.getElementById(optionId(activeIndex.value))?.scrollIntoView?.({ block: 'nearest' });\n });\n}\n\nfunction onTriggerKeydown(event: KeyboardEvent) {\n if (props.disabled) return;\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n if (!open.value) openDropdown();\n else moveActive(1);\n break;\n case 'ArrowUp':\n event.preventDefault();\n if (!open.value) openDropdown();\n else moveActive(-1);\n break;\n case 'Home':\n if (open.value) {\n event.preventDefault();\n activeIndex.value = 0;\n }\n break;\n case 'End':\n if (open.value) {\n event.preventDefault();\n activeIndex.value = visibleOptions.value.length - 1;\n }\n break;\n case 'Enter':\n case ' ':\n case 'Spacebar':\n event.preventDefault();\n if (!open.value) {\n openDropdown();\n } else if (activeIndex.value >= 0) {\n const option = visibleOptions.value[activeIndex.value];\n if (option) selectOption(option.value);\n }\n break;\n case 'Escape':\n if (open.value) {\n event.preventDefault();\n // Stop here so a DsModal (or anything else) listening for Escape on\n // `document` doesn't also close — the dropdown is the innermost layer.\n event.stopPropagation();\n closeDropdown();\n }\n break;\n case 'Tab':\n // Let the browser's default Tab handling move focus; just collapse the\n // popup without stealing focus back.\n if (open.value) closeDropdown(false);\n break;\n default:\n break;\n }\n}\n\n// Keydown on the search input. Only the navigation keys are intercepted; Space,\n// Home, End and printable characters fall through to the browser so text editing\n// (typing a space, jumping the caret) keeps working inside the input.\nfunction onSearchKeydown(event: KeyboardEvent) {\n if (props.disabled) return;\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n moveActive(1);\n break;\n case 'ArrowUp':\n event.preventDefault();\n moveActive(-1);\n break;\n case 'Enter':\n event.preventDefault();\n if (activeIndex.value >= 0) {\n const option = visibleOptions.value[activeIndex.value];\n if (option) selectOption(option.value);\n }\n break;\n case 'Escape':\n event.preventDefault();\n event.stopPropagation();\n closeDropdown();\n break;\n case 'Tab':\n if (open.value) closeDropdown(false);\n break;\n default:\n break;\n }\n}\n\nfunction onSearchInput(event: Event) {\n query.value = (event.target as HTMLInputElement).value;\n // Always emit so a remote consumer can react; local filtering (when not\n // `remote`) happens reactively via `filteredOptions`.\n emit(SEARCH, query.value);\n}\n\n// Keep activeIndex valid whenever the rendered list changes underneath it —\n// local filtering as the user types, remote results arriving, or loading\n// toggling. Not triggered by opening (open is not a dependency of\n// visibleOptions), so it never fights findInitialActiveIndex.\nwatch(visibleOptions, (opts) => {\n if (!open.value) return;\n if (opts.length === 0) activeIndex.value = -1;\n else if (activeIndex.value < 0 || activeIndex.value >= opts.length) activeIndex.value = 0;\n});\n\nfunction onDocumentMousedown(event: MouseEvent) {\n if (rootRef.value && !rootRef.value.contains(event.target as Node)) {\n closeDropdown(false);\n }\n}\n\nwatch(open, (isOpen) => {\n if (typeof document === 'undefined') return;\n if (isOpen) document.addEventListener('mousedown', onDocumentMousedown);\n else document.removeEventListener('mousedown', onDocumentMousedown);\n});\n\nwatch(\n () => props.disabled,\n (isDisabled) => {\n if (isDisabled && open.value) closeDropdown(false);\n }\n);\n\nonBeforeUnmount(() => {\n if (typeof document === 'undefined') return;\n document.removeEventListener('mousedown', onDocumentMousedown);\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/spacing.scss' as s;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/shadow.scss' as sh;\n@use '../../assets/css/motion.scss' as m;\n@use '../../assets/css/icons.scss' as i;\n\n.ds-select {\n position: relative;\n display: inline-flex; // box width comes from the width step/value (default: auto)\n flex-direction: column;\n\n font-family: t.$font-family;\n\n // Width steps — a stable, fixed box width from the token scale so the control\n // does not resize to the selected label. 'auto' restores shrink-to-content;\n // 'full' fills the container. A custom number/CSS-string width is set inline.\n &--w-small {\n width: s.control-width('small');\n }\n &--w-medium {\n width: s.control-width('medium');\n }\n &--w-large {\n width: s.control-width('large');\n }\n &--w-full {\n width: 100%;\n }\n &--w-auto {\n width: auto;\n }\n\n &__control {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: s.spacing(1);\n\n width: 100%;\n height: s.control-height('medium'); // DS Select.jsx: height 32 (maxHeight 36)\n padding: 0 s.spacing(1.5);\n\n background-color: colors.$slate-bg; // DS --primary-background (grey pill, rest)\n border-radius: r.$radius-control;\n\n transition: background-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n &:hover:not(.is-disabled) {\n background-color: colors.$slate-tint; // DS --primary-tint (hover)\n }\n\n &.is-disabled {\n opacity: 0.6; // DS Select.jsx: opacity disabled ? 0.6 : 1\n cursor: not-allowed;\n }\n\n // filterSummary flips grey → accent once something is selected (DS: bg is\n // accent-tint at rest, accent-bg on hover, only when isFilter && hasSelection).\n &.is-filter-selected {\n background-color: colors.$accent-tint;\n\n &:hover:not(.is-disabled) {\n background-color: colors.$accent-bg;\n }\n\n .ds-select__summary {\n color: colors.$accent-main;\n }\n }\n }\n\n &__trigger {\n display: flex;\n align-items: center;\n flex: 1 1 auto;\n min-width: 0;\n\n height: 100%;\n padding: 0;\n\n font-family: t.$font-family;\n text-align: left;\n\n background: transparent;\n border: 0;\n outline: none;\n\n cursor: pointer;\n\n &:disabled {\n cursor: not-allowed;\n }\n }\n\n &__summary {\n overflow: hidden;\n\n color: colors.$slate-main; // DS --primary-main\n font-weight: t.$weight-medium; // DS: fontWeight 500 (constant)\n font-size: t.font-size('body');\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n\n &__clear {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n\n padding: 0;\n\n color: colors.$accent-main;\n\n background: transparent;\n border: 0;\n\n cursor: pointer;\n\n .material-icons {\n font-size: i.icon-size(20); // DS: close icon fontSize 20\n }\n\n &:disabled {\n cursor: not-allowed;\n }\n }\n\n &__chevron {\n flex: none;\n\n color: colors.$slate-main; // DS --primary-main\n font-size: i.icon-size(18); // DS: chevron fontSize 18\n line-height: 1;\n\n transition: transform m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n }\n\n &.is-open &__chevron {\n transform: rotate(180deg);\n }\n\n // Popup wrapper: holds the (optional) search field pinned above the scrolling\n // listbox. Owns the box chrome that used to live on the listbox itself.\n &__popup {\n position: absolute;\n top: calc(100% + #{s.spacing(0.5)});\n left: 0;\n z-index: 20; // above page content, below DsModal's 2001\n\n display: flex;\n flex-direction: column;\n min-width: 100%;\n overflow: hidden; // clip the listbox / search corners to the radius\n\n background-color: colors.$white;\n border: 1px solid colors.$slate-tint;\n border-radius: r.$radius-control;\n box-shadow: sh.$shadow-dropdown;\n }\n\n &__search {\n display: flex;\n align-items: center;\n gap: s.spacing(1);\n\n padding: s.spacing(1) s.spacing(1.5);\n\n border-bottom: 1px solid colors.$slate-tint;\n }\n\n &__search-icon {\n flex: none;\n\n color: colors.$slate-soft;\n font-size: i.icon-size(18);\n line-height: 1;\n }\n\n &__search-input {\n flex: 1 1 auto;\n min-width: 0;\n\n padding: 0;\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n\n background: transparent;\n border: 0;\n outline: none;\n\n &::placeholder {\n color: colors.$slate-soft;\n }\n }\n\n &__listbox {\n max-height: 260px; // DS Select.jsx: maxHeight 260\n margin: 0;\n padding: s.spacing(0.5) 0;\n overflow-y: auto;\n\n list-style: none;\n }\n\n &__option {\n display: flex;\n align-items: center;\n gap: s.spacing(1);\n\n padding: s.spacing(1) s.spacing(2);\n\n color: colors.$slate-main; // DS --primary-main\n font-size: t.font-size('body');\n white-space: nowrap;\n\n cursor: pointer;\n\n &:hover,\n &.is-active {\n background-color: colors.$slate-tint;\n }\n\n // Bold + accent text on selection, but only when there's no checkbox glyph\n // doing that job instead (DS SelectOption: `isSelected && !showCheckbox`).\n &.is-selected:not(.ds-select__option--checkbox) {\n color: colors.$accent-main;\n font-weight: t.$weight-bold;\n }\n\n // DsCheckbox's root inherits this scope's data-v-* attribute as a child\n // component root (standard Vue scoped-CSS behaviour), so a plain class\n // selector reaches it — but its own `.is-disabled { opacity: .5 }` rule has\n // the same specificity, making the winner order-dependent on CSS emission.\n // The extra ancestor here breaks the tie deterministically: `disabled`\n // keeps the decorative glyph's native input out of the tab order without\n // letting DsCheckbox's disabled-state opacity mute the selected/unselected look.\n .ds-select__option-check.is-disabled {\n opacity: 1;\n }\n }\n\n &__option-check {\n flex: none;\n pointer-events: none; // decorative only — the <li> owns click/keyboard selection\n }\n\n &__option-icon {\n flex: none;\n font-size: i.icon-size(20);\n }\n\n &__option-label {\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n &__option-sublabel {\n color: colors.$slate-soft; // DS --primary-secondary-text\n }\n\n // Group run — a real ARIA group (role=\"group\"). The divider sits on the\n // container so it separates groups (and grouped from ungrouped rows); the very\n // first run never shows a stray line at the top of the popup.\n &__group {\n border-top: 1px solid colors.$slate-tint;\n\n &:first-child {\n border-top: none;\n }\n }\n\n // Group header — the non-selectable label naming the group (referenced by the\n // group's aria-labelledby).\n &__group-header {\n display: block;\n padding: s.spacing(1) s.spacing(2) s.spacing(0.5);\n\n color: colors.$slate-soft;\n font-size: t.font-size('caption');\n font-weight: t.$weight-medium;\n }\n\n // Loading row — centred spinner, distinct from the empty state. Not selectable.\n &__loading {\n display: flex;\n align-items: center;\n justify-content: center;\n\n padding: s.spacing(2);\n }\n\n // Empty state row — not selectable (role=presentation), muted like a sublabel.\n &__empty {\n padding: s.spacing(1) s.spacing(2);\n\n color: colors.$slate-soft;\n font-size: t.font-size('body');\n }\n}\n</style>\n","<template>\n <tr\n class=\"ds-row\"\n :class=\"{ 'is-selected': selected, 'is-clickable': clickable }\"\n :tabindex=\"clickable ? 0 : undefined\"\n @click=\"onClick\"\n @keydown=\"onKeydown\"\n >\n <slot />\n </tr>\n</template>\n\n<script setup lang=\"ts\">\nimport { CLICK } from '@shared/constants/events';\n\nconst props = defineProps({\n selected: { type: Boolean, default: false },\n clickable: { type: Boolean, default: false },\n});\n\nconst emit = defineEmits([CLICK]);\n\n// Only interactive rows emit — a plain row is not a click target.\nfunction onClick() {\n if (props.clickable) emit(CLICK);\n}\n\nfunction onKeydown(e: KeyboardEvent) {\n if (!props.clickable) return;\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n emit(CLICK);\n }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/motion.scss' as m;\n\n.ds-row {\n background-color: colors.$white;\n transition: background-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n &.is-clickable {\n cursor: pointer;\n }\n\n &:hover {\n background-color: colors.$slate-bg;\n }\n\n // Selected wins over hover.\n &.is-selected,\n &.is-selected:hover {\n background-color: colors.$accent-bg;\n }\n\n &.is-clickable:focus-visible {\n outline: 2px solid colors.$accent-mid;\n outline-offset: -2px;\n }\n\n // A pinned (sticky) DsCell paints its own opaque background so scrolling cells\n // don't show through — which means it can't inherit the row's hover/selected\n // colour. Mirror the row state onto it here (:deep reaches the slotted DsCell,\n // in its own scope). Selected already tracked by DsCell.is-selected; this adds\n // the hover state the cell can't observe on its own.\n &:hover :deep(.ds-cell.is-pinned) {\n background-color: colors.$slate-bg;\n }\n &.is-selected:hover :deep(.ds-cell.is-pinned) {\n background-color: colors.$accent-bg;\n }\n}\n</style>\n","<template>\n <td\n class=\"ds-cell\"\n :class=\"{\n 'is-accent': first && selected,\n 'is-truncated': truncate,\n 'is-selected': selected,\n 'is-pinned': !!pinned,\n [`is-pinned--${pinned}`]: !!pinned,\n }\"\n :style=\"cellStyle\"\n >\n <slot />\n </td>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, type PropType } from 'vue';\nimport { DS_TABLE_PIN, type DsTablePin } from '../DsTable/DsTable.types';\n\nconst props = defineProps({\n first: { type: Boolean, default: false },\n selected: { type: Boolean, default: false },\n width: { type: [String, Number] as PropType<string | number | null>, default: null },\n // Opt-in single-line ellipsis. Default off preserves the 72px multi-line cell.\n truncate: { type: Boolean, default: false },\n // Pin to the left/right edge (position: sticky) for horizontal scroll.\n pinned: { type: String as PropType<DsTablePin | null>, default: null },\n // Cumulative offset from the pinned edge when stacking multiple pins per side\n // (number → px, or any CSS length). Defaults to 0 (single pin per side).\n pinnedOffset: {\n type: [String, Number] as PropType<string | number | null>,\n default: null,\n },\n});\n\nconst toLength = (v: string | number) => (typeof v === 'number' ? `${v}px` : v);\n\nconst cellStyle = computed(() => {\n const style: Record<string, string> = {};\n if (props.width != null) style.width = toLength(props.width);\n if (props.pinned) {\n style.position = 'sticky';\n const offset = props.pinnedOffset == null ? '0px' : toLength(props.pinnedOffset);\n style[props.pinned === DS_TABLE_PIN.LEFT ? 'left' : 'right'] = offset;\n }\n return Object.keys(style).length ? style : undefined;\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/spacing.scss' as s;\n@use '../../assets/css/typography.scss' as t;\n\n.ds-cell {\n height: 72px; // DS DsTable.jsx DsCell\n padding: s.spacing(1); // 8\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n\n overflow: hidden;\n\n border-bottom: 1px solid colors.$slate-tint;\n\n // First cell of a selected row draws the 3px accent left rail (DS).\n &.is-accent {\n border-left: 3px solid colors.$accent-main; // DS DsTable.jsx: 3px rail\n }\n\n // Opt-in truncation: reuses the existing `overflow: hidden`. Default (off)\n // keeps the multi-line behavior the 72px cell height implies.\n &.is-truncated {\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n\n // Pinned (sticky) cell. position/left/right come from the inline style; here we\n // own the opaque background (so scrolling cells never bleed through) and the\n // stacking order — below the compound sticky header (z-index 10), above normal\n // body cells. The row background (white / accent when selected) is mirrored so\n // the frozen column matches its row; row-hover is handled from DsRow via :deep,\n // and the scroll-toggled edge shadow is driven by DsTable (also via :deep).\n &.is-pinned {\n z-index: 5;\n background-color: colors.$white;\n\n &.is-selected {\n background-color: colors.$accent-bg;\n }\n }\n}\n</style>\n","// DsCustomTag — the DS \"CustomTag\": static status chip (non-interactive).\n// Types mirror the DS source (DsCustomTag.jsx TYPES). `danger` maps to the\n// `error-*` token family and `accent` maps to the `info-*` token family —\n// both are intentional DS naming quirks, kept as-is (do not rename the prop\n// values to match the token family names).\n\nexport const DS_CUSTOM_TAG_TYPES = {\n SUCCESS: 'success',\n WARNING: 'warning',\n DANGER: 'danger', // maps to $error-* tokens, not $danger-*\n INFO: 'info',\n DRAFT: 'draft', // the only solid-fill variant\n ACCENT: 'accent', // maps to $info-* tokens, not $accent-*\n} as const;\nexport type DsCustomTagType = (typeof DS_CUSTOM_TAG_TYPES)[keyof typeof DS_CUSTOM_TAG_TYPES];\n","<template>\n <span class=\"ds-custom-tag\" :class=\"[`ds-custom-tag--${type}`, { 'is-round': round }]\">\n <slot />\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport { type PropType } from 'vue';\nimport { DS_CUSTOM_TAG_TYPES, type DsCustomTagType } from './DsCustomTag.types';\n\ndefineProps({\n type: {\n type: String as PropType<DsCustomTagType>,\n default: DS_CUSTOM_TAG_TYPES.INFO,\n },\n // Fully rounded (radius-full) sides vs the default radius-control.\n round: Boolean,\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/spacing.scss' as s;\n\n.ds-custom-tag {\n display: inline-flex;\n align-items: center;\n\n padding: 3px s.spacing(1); // 3px = literal, DS DsCustomTag.jsx\n\n font-family: t.$font-family;\n font-weight: t.$weight-bold;\n font-size: t.font-size('caption');\n line-height: 18px; // literal, DS DsCustomTag.jsx\n\n border: 1px solid;\n border-radius: r.$radius-control;\n\n white-space: nowrap;\n\n &.is-round {\n border-radius: r.$radius-full;\n }\n\n /* ---------- Types (tint bg + shadow text, except `draft` = solid fill) ---------- */\n &--success {\n color: colors.$success-shadow;\n border-color: colors.$success-shadow;\n background-color: colors.$success-tint;\n }\n &--warning {\n color: colors.$warning-shadow;\n border-color: colors.$warning-shadow;\n background-color: colors.$warning-tint;\n }\n &--danger {\n // DS prop value is `danger`, wired to the `error-*` token family (kept as-is).\n color: colors.$error-shadow;\n border-color: colors.$error-shadow;\n background-color: colors.$error-tint;\n }\n &--info {\n color: colors.$slate-soft;\n border-color: colors.$slate-soft;\n background-color: colors.$slate-tint;\n }\n &--draft {\n // The only solid-fill variant: white text on a solid slate-soft fill.\n color: colors.$white;\n border-color: colors.$slate-soft;\n background-color: colors.$slate-soft;\n }\n &--accent {\n // DS prop value is `accent`, wired to the `info-*` token family (kept as-is).\n color: colors.$info-shadow;\n border-color: colors.$info-shadow;\n background-color: colors.$info-tint;\n }\n}\n</style>\n","<template>\n <span\n class=\"ds-pill\"\n :class=\"{ 'ds-pill--has-icon': icon }\"\n :style=\"background ? { background } : undefined\"\n >\n <span class=\"ds-pill__text\"><slot /></span>\n\n <button\n v-if=\"icon\"\n type=\"button\"\n class=\"ds-pill__icon\"\n :aria-label=\"iconLabel || undefined\"\n @click=\"emit(CLICK)\"\n >\n <i class=\"material-icons\" aria-hidden=\"true\">{{ icon }}</i>\n </button>\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport { CLICK } from '@shared/constants/events';\n\ndefineProps({\n // Material Icons ligature name; when set, renders a trailing icon button.\n icon: {\n type: String,\n default: '',\n },\n // Free-form CSS color (e.g. a dynamic tag/folder color); when empty, falls\n // back to the default token background ($slate-soft).\n background: {\n type: String,\n default: '',\n },\n // Accessible name for the trailing icon button (icon-only control).\n iconLabel: {\n type: String,\n default: '',\n },\n});\n\nconst emit = defineEmits([CLICK]);\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/spacing.scss' as s;\n@use '../../assets/css/icons.scss' as i;\n\n.ds-pill {\n display: inline-flex;\n align-items: center;\n\n height: 20px; // DS DsPill.jsx\n padding: 0 s.spacing(1);\n\n color: colors.$white;\n font-family: t.$font-family;\n font-weight: t.$weight-bold;\n font-size: t.font-size('caption');\n\n border-radius: r.$radius-pill;\n\n background: colors.$slate-soft;\n\n user-select: none;\n\n // Long tag/folder names truncate instead of stretching the pill. `min-width: 0`\n // is the flexbox gotcha — without it a flex child won't shrink below its content\n // size, so the ellipsis never triggers inside the inline-flex pill. The consumer\n // bounds the pill's width (own wrapper/style); the pill just degrades gracefully.\n &__text {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n // The trailing icon button eats into the right inset (DS DsPill.jsx).\n &--has-icon {\n padding: 0 2px 0 s.spacing(1); // 2px: DS DsPill.jsx trailing icon inset\n gap: 2px; // DS DsPill.jsx\n }\n\n &__icon {\n display: inline-flex;\n align-items: center;\n\n padding: 0;\n\n color: inherit;\n\n background: none;\n border: 0;\n\n cursor: pointer;\n\n .material-icons {\n font-size: i.icon-size(14);\n }\n }\n}\n</style>\n","<template>\n <button type=\"button\" class=\"ds-pill-with-border\" @click=\"emit(CLICK)\">\n <span v-if=\"$slots.icon\" class=\"ds-pill-with-border__icon\"><slot name=\"icon\" /></span>\n <span class=\"ds-pill-with-border__label\"><slot /></span>\n </button>\n</template>\n\n<script setup lang=\"ts\">\nimport { CLICK } from '@shared/constants/events';\n\n// No props: content is slot-driven (see DsPillWithBorder.types.ts).\nconst emit = defineEmits([CLICK]);\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/spacing.scss' as s;\n\n.ds-pill-with-border {\n display: inline-flex;\n align-items: center;\n gap: s.spacing(1); // 8\n\n // Let a consumer-supplied width cap actually bound the chip (the table cell\n // passes a max-width via attr fallthrough on this single root button).\n max-width: 100%;\n\n // Reset default <button> appearance so it reads as the DS chip, not a native button.\n margin: 0;\n appearance: none;\n -webkit-appearance: none;\n\n padding: 6px 12px 6px 8px; // 6px literal (DS); 12px = s.spacing(1.5); 8px = s.spacing(1)\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n\n background: colors.$white;\n border: 1px solid colors.$slate-tint;\n border-radius: r.$radius-full; // DS used a literal 100px; radius-full (9999px) rounds identically on this height-less box\n outline: none;\n\n cursor: pointer;\n\n &:focus-visible {\n outline: 2px solid colors.$accent-mid;\n outline-offset: 1px;\n }\n\n &__icon {\n display: inline-flex;\n }\n\n // Single-line by contract: a width-constrained pill truncates instead of\n // wrapping and breaking the radius-full shape. `min-width: 0` lets the flex\n // item shrink below its content size so the ellipsis can trigger.\n &__label {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n</style>\n","<!--\n Root layout note (kept out of the template body: a root-level comment would\n make the SFC multi-root and break attribute fall-through):\n - No legend (default): the track IS the root, byte-for-byte the original\n render. Opting into a legend is purely additive — it never touches this path.\n - Legend on: the same unchanged track wrapped with a color↔name key beneath it.\n Either way, `inheritAttrs` is off and `$attrs` is re-applied to the track, so\n consumer attrs always land on the `role=\"img\"` element, not the wrapper.\n Segments are keyed on `name + index` (not a stable id) on purpose: they are a\n static data-viz snapshot, not a reorderable list, so position is the identity.\n-->\n<template>\n <div\n v-if=\"!hasLegend\"\n v-bind=\"$attrs\"\n class=\"ds-segmented-progress-bar\"\n role=\"img\"\n :aria-label=\"label || undefined\"\n :style=\"{ height: `${height}px` }\"\n >\n <div\n v-for=\"(seg, idx) in segments\"\n :key=\"`${seg.name ?? ''}-${idx}`\"\n class=\"ds-segmented-progress-bar__segment\"\n :style=\"{\n width: `${total > 0 ? (seg.amount * 100) / total : 0}%`,\n backgroundColor: seg.color,\n }\"\n :title=\"seg.name || undefined\"\n />\n </div>\n\n <div v-else class=\"ds-segmented-progress-bar-wrapper\">\n <div\n v-bind=\"$attrs\"\n class=\"ds-segmented-progress-bar\"\n role=\"img\"\n :aria-label=\"label || undefined\"\n :style=\"{ height: `${height}px` }\"\n >\n <div\n v-for=\"(seg, idx) in segments\"\n :key=\"`${seg.name ?? ''}-${idx}`\"\n class=\"ds-segmented-progress-bar__segment\"\n :style=\"{\n width: `${total > 0 ? (seg.amount * 100) / total : 0}%`,\n backgroundColor: seg.color,\n }\"\n :title=\"seg.name || undefined\"\n />\n </div>\n\n <!--\n The auto legend is a VISUAL key: it maps each swatch's color to its name.\n That mapping duplicates what the track's `aria-label` summary already\n conveys, so it is `aria-hidden` — the label stays the single authoritative\n a11y summary and a screen reader never hears the segment names twice\n (which would be the \"confusing duplicate semantics\" this follow-up warns\n against). A custom `#legend` slot owns its own semantics and is NOT forced\n hidden — the consumer decides whether their content is decorative.\n -->\n <slot name=\"legend\">\n <ul class=\"ds-segmented-progress-bar__legend\" aria-hidden=\"true\">\n <li\n v-for=\"(seg, idx) in legendSegments\"\n :key=\"`${seg.name}-${idx}`\"\n class=\"ds-segmented-progress-bar__legend-item\"\n >\n <span\n class=\"ds-segmented-progress-bar__legend-swatch\"\n :style=\"{ backgroundColor: seg.color }\"\n />\n <span class=\"ds-segmented-progress-bar__legend-label\">{{ seg.name }}</span>\n </li>\n </ul>\n </slot>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, useSlots, type PropType } from 'vue';\nimport type { DsProgressSegment } from './DsSegmentedProgressBar.types';\n\n// Consumer attrs (class, style, id, data-*, listeners, extra aria) must land on\n// the `role=\"img\"` track — the semantic element — and land there consistently\n// whether or not the legend wrapper exists. So we opt out of automatic\n// inheritance and re-apply `$attrs` onto the track in both branches.\ndefineOptions({ inheritAttrs: false });\n\nconst props = defineProps({\n segments: {\n type: Array as PropType<DsProgressSegment[]>,\n default: () => [],\n },\n total: { type: Number, default: 100 },\n height: { type: Number, default: 8 },\n // Accessible name for the whole track — the DS source has zero ARIA on this\n // element, so role=\"img\" + this label are the only a11y surface. Left empty\n // by default: omitted from the DOM until the consumer supplies a meaningful\n // summary (e.g. \"55% verified, 25% risky, 20% invalid\").\n label: { type: String, default: '' },\n // Opt-in: render a legend (color swatch + name) summarising every segment\n // below the track. Off by default so the render stays identical. A `#legend`\n // slot also opts in and replaces the auto legend with custom content.\n showLegend: { type: Boolean, default: false },\n});\n\nconst slots = useSlots();\n\n// Only named segments belong in a color↔name key — a nameless swatch has no\n// mapping to show. Consumers needing full control use the `#legend` slot.\nconst legendSegments = computed(() => props.segments.filter((seg) => !!seg.name));\n\n// The legend region appears when a custom `#legend` slot opts in, or when\n// `showLegend` is on AND there is at least one named segment to key. Guarding on\n// the count means \"showLegend with nothing to show\" degrades to the plain track\n// (no empty list, no dangling wrapper gap) rather than rendering an empty key.\nconst hasLegend = computed(\n () => !!slots.legend || (props.showLegend && legendSegments.value.length > 0)\n);\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/spacing.scss' as s;\n@use '../../assets/css/typography.scss' as t;\n\n.ds-segmented-progress-bar {\n display: flex;\n width: 100%;\n overflow: hidden;\n\n background: colors.$slate-tint;\n border-radius: r.$radius-card; // DS --radius-md\n\n &__segment {\n height: 100%;\n }\n\n // Wrapper only exists when a legend is rendered; the track keeps full width.\n &-wrapper {\n display: flex;\n flex-direction: column;\n gap: s.spacing(1); // 8px between the track and its legend\n }\n\n &__legend {\n display: flex;\n flex-wrap: wrap;\n gap: s.spacing(0.5) s.spacing(2); // 4px row · 16px column\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n &__legend-item {\n display: inline-flex;\n align-items: center;\n gap: s.spacing(0.5); // 4px between swatch and label\n }\n\n &__legend-swatch {\n flex-shrink: 0;\n width: s.spacing(1.5); // 12px square, echoing the caption cap-height\n height: s.spacing(1.5);\n border-radius: r.$radius-control; // DS --radius-sm — mirrors the bar's language\n }\n\n &__legend-label {\n font-family: t.$font-family;\n font-size: t.font-size('caption'); // 12px\n font-weight: t.$weight-regular;\n line-height: t.$line-height-tight;\n color: colors.$slate-soft;\n }\n}\n</style>\n","<template>\n <div class=\"ds-empty-state\" :class=\"{ 'is-rounded': roundedBorder }\">\n <div v-if=\"$slots['table-header']\" class=\"ds-empty-state__header\">\n <slot name=\"table-header\" />\n </div>\n\n <div class=\"ds-empty-state__body\">\n <img v-if=\"image\" :src=\"image\" alt=\"\" class=\"ds-empty-state__image\" />\n <p class=\"ds-empty-state__title\">{{ title }}</p>\n <p v-if=\"subtitle\" class=\"ds-empty-state__subtitle\">{{ subtitle }}</p>\n <slot name=\"button\" />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\ndefineProps({\n // Illustration URL; decorative (rendered with alt=\"\").\n image: { type: String, default: '' },\n title: { type: String, default: '' },\n subtitle: { type: String, default: '' },\n // DS `radius-card-lg` corners instead of the default square edge.\n roundedBorder: { type: Boolean, default: false },\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/spacing.scss' as s;\n\n.ds-empty-state {\n display: flex;\n flex-direction: column;\n gap: s.spacing(4); // 32\n\n padding: s.spacing(3); // 24\n\n border: 1px solid colors.$slate-tint;\n border-radius: 0;\n\n &.is-rounded {\n border-radius: r.$radius-card-lg;\n }\n\n &__body {\n display: flex;\n flex-direction: column;\n align-items: center;\n text-align: center;\n gap: s.spacing(1);\n }\n\n &__image {\n max-height: 200px; // DS literal\n max-width: 100%;\n }\n\n &__title {\n font-family: t.$font-family;\n font-weight: t.$weight-bold;\n font-size: t.font-size('h1');\n color: colors.$slate-main;\n margin: 0;\n }\n\n &__subtitle {\n font-family: t.$font-family;\n font-size: t.font-size('body');\n line-height: t.$line-height-body;\n // NOTE: subtitle is NOT muted — DS uses primary-main, do not use slate-soft.\n color: colors.$slate-main;\n white-space: pre-line;\n margin: 0;\n\n // Cap the measure so long subtitles don't stretch edge-to-edge. `ch` is\n // relative to the font (not a hardcoded px), giving ~optimal reading width;\n // margin-inline: auto keeps it centered under the (centered) title.\n max-width: 46ch;\n margin-inline: auto;\n }\n}\n</style>\n","<template>\n <nav class=\"ds-paginator\" :aria-label=\"paginationLabel || undefined\">\n <span v-if=\"limitMessage\" class=\"ds-paginator__limit-message\">{{ limitMessage }}</span>\n\n <div class=\"ds-paginator__pager\">\n <DsButton\n variant=\"tertiary\"\n circle\n size=\"medium\"\n left-icon=\"chevron_left\"\n :aria-label=\"prevLabel || undefined\"\n :disabled=\"currentPage <= 1\"\n @click=\"goTo(currentPage - 1)\"\n />\n\n <template v-for=\"item in pages\" :key=\"item\">\n <!-- Sentinel entries ('…l' / '…r') are the only string items in `pages`;\n every real page is a number, so a typeof check both matches the DS\n ellipsis condition and lets vue-tsc narrow `item` to number below. -->\n <span v-if=\"typeof item === 'string'\" class=\"ds-paginator__ellipsis\" aria-hidden=\"true\"\n >…</span\n >\n <button\n v-else\n type=\"button\"\n class=\"ds-paginator__page\"\n :class=\"{ 'is-active': item === currentPage }\"\n :aria-current=\"item === currentPage ? 'page' : undefined\"\n @click=\"goTo(item)\"\n >\n {{ item }}\n </button>\n </template>\n\n <DsButton\n variant=\"tertiary\"\n circle\n size=\"medium\"\n left-icon=\"chevron_right\"\n :aria-label=\"nextLabel || undefined\"\n :disabled=\"currentPage >= pageCount\"\n @click=\"goTo(currentPage + 1)\"\n />\n </div>\n\n <select\n class=\"ds-paginator__page-size\"\n :aria-label=\"pageSizeLabel || undefined\"\n :value=\"pageSize\"\n @change=\"onSizeChange\"\n >\n <option v-for=\"s in pageSizes\" :key=\"s\" :value=\"s\">{{ s }}{{ pageSizeSuffix }}</option>\n </select>\n </nav>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, type PropType } from 'vue';\nimport { DsButton } from '@shared/ui/DsButton';\nimport { PAGE_CHANGE, PAGE_SIZE_CHANGE } from '@shared/constants/events';\n\n// Left/right ellipsis sentinels for the page window (see `pages` below). Typed\n// as literals (`as const`) so `pages` is `(number | '…l' | '…r')[]`, not `string[]`.\nconst ELLIPSIS_LEFT = '…l' as const;\nconst ELLIPSIS_RIGHT = '…r' as const;\n\nconst props = defineProps({\n total: { type: Number, required: true },\n pageSize: { type: Number, default: 25 },\n currentPage: { type: Number, default: 1 },\n pageSizes: { type: Array as PropType<number[]>, default: () => [10, 25, 50, 100] },\n limitMessage: { type: String, default: '' },\n // Appended to each page-size option label (e.g. consumer passes \"/page\").\n pageSizeSuffix: { type: String, default: '' },\n // aria-labels for the icon-only chevron buttons — DS has none; empty unless\n // the consumer supplies one (see the a11y note on `.types.ts`).\n prevLabel: { type: String, default: '' },\n nextLabel: { type: String, default: '' },\n paginationLabel: { type: String, default: '' },\n pageSizeLabel: { type: String, default: '' },\n});\n\n// Runtime array (not the type-only form) — see DsButton's CLICK for the same\n// rationale: the SFC compiler can't resolve `typeof PAGE_CHANGE` to a literal,\n// so declaring these at runtime is what keeps them out of attrs fallthrough.\nconst emit = defineEmits([PAGE_CHANGE, PAGE_SIZE_CHANGE]);\n\nconst pageCount = computed(() => Math.max(1, Math.ceil(props.total / props.pageSize)));\n\n// Page-window algorithm (DS pagination pattern), replicated exactly:\n// ≤7 pages → show them all. Otherwise: always 1; a left ellipsis once\n// currentPage > 4; the pages currentPage-2..currentPage+2 (clamped to\n// [2, pageCount-1]); a right ellipsis while currentPage < pageCount-3;\n// always pageCount.\nconst pages = computed<(number | typeof ELLIPSIS_LEFT | typeof ELLIPSIS_RIGHT)[]>(() => {\n const count = pageCount.value;\n\n if (count <= 7) {\n return Array.from({ length: count }, (_, index) => index + 1);\n }\n\n const current = props.currentPage;\n const items: (number | typeof ELLIPSIS_LEFT | typeof ELLIPSIS_RIGHT)[] = [1];\n\n if (current > 4) items.push(ELLIPSIS_LEFT);\n\n const start = Math.max(2, current - 2);\n const end = Math.min(count - 1, current + 2);\n for (let page = start; page <= end; page++) {\n items.push(page);\n }\n\n if (current < count - 3) items.push(ELLIPSIS_RIGHT);\n\n items.push(count);\n\n return items;\n});\n\nfunction goTo(page: number) {\n if (page < 1 || page > pageCount.value) return;\n emit(PAGE_CHANGE, page);\n}\n\nfunction onSizeChange(event: Event) {\n // `HTMLSelectElement` isn't in this repo's eslint `no-undef` globals allowlist\n // (only `HTMLElement`/`HTMLInputElement` are — see eslint.config.mjs); narrow\n // via an inline `.value` shape instead of adding a new global for one cast.\n const target = event.target as HTMLElement & { value: string };\n emit(PAGE_SIZE_CHANGE, Number(target.value));\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/spacing.scss' as s;\n\n.ds-paginator {\n position: relative;\n display: flex;\n align-items: center;\n gap: s.spacing(2);\n\n font-family: t.$font-family;\n\n &__limit-message {\n color: colors.$slate-soft;\n font-size: t.font-size('body');\n }\n\n &__pager {\n display: flex;\n align-items: center;\n gap: 2px; // DS literal, sub-scale\n }\n\n &__page {\n min-width: s.control-height('medium'); // 32×32\n height: s.control-height('medium'); // 32×32\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n padding: 0;\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n\n background: none;\n border: 0;\n border-radius: r.$radius-control;\n\n cursor: pointer;\n\n &:hover {\n color: colors.$accent-main;\n }\n\n &.is-active {\n color: colors.$accent-main;\n font-weight: t.$weight-bold;\n }\n }\n\n &__ellipsis {\n min-width: s.control-height('medium');\n text-align: center;\n color: colors.$slate-main;\n }\n\n &__page-size {\n height: s.control-height('medium');\n padding: 0 s.spacing(1);\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n\n border: 1px solid colors.$slate-tint;\n border-radius: r.$radius-control;\n }\n}\n</style>\n","<template>\n <button\n type=\"button\"\n class=\"ds-menu-item\"\n :class=\"{ 'is-active': active, 'is-collapsed': collapsed }\"\n :aria-current=\"active ? 'page' : undefined\"\n :title=\"text || undefined\"\n @click=\"handleClick\"\n >\n <i class=\"ds-menu-item__icon material-icons\" aria-hidden=\"true\">{{ icon }}</i>\n <span v-if=\"text || $slots.default\" class=\"ds-menu-item__label\">\n <slot>{{ text }}</slot>\n </span>\n </button>\n</template>\n\n<script setup lang=\"ts\">\nimport { SELECT } from '@shared/constants/events';\n\ndefineProps({\n // Material Icons ligature name (rendered via <i class=\"material-icons\">).\n icon: { type: String, required: true },\n // Row label; a default slot overrides it. Hidden (not removed) when collapsed.\n text: { type: String, default: '' },\n // Current row: white surface + tint border, aria-current=\"page\".\n active: Boolean,\n // Icon-only collapsed rail.\n collapsed: Boolean,\n});\n\n// The DS ships this as a <div onClick> with no keyboard/ARIA. We render a real\n// <button> (keyboard-reachable, focus ring) and emit the semantic SELECT — the\n// native @click still falls through for consumers that prefer it.\nconst emit = defineEmits([SELECT]);\n\nfunction handleClick(event: MouseEvent) {\n emit(SELECT, event);\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/spacing.scss' as s;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n@use '../../assets/css/icons.scss' as i;\n\n.ds-menu-item {\n display: flex;\n align-items: center;\n gap: s.spacing(1);\n width: 100%;\n min-height: 36px;\n padding: s.spacing(1);\n\n color: colors.$slate-main;\n font-family: t.$font-family;\n font-size: t.font-size('body');\n line-height: 20px;\n text-align: left;\n\n border: 1px solid transparent;\n border-radius: r.$radius-control;\n background-color: transparent;\n\n cursor: pointer;\n user-select: none;\n\n transition:\n background-color m.$duration-default m.$easing,\n border-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n // active always wins over hover (DS: active bg stays white on hover).\n &:hover:not(.is-active) {\n background-color: colors.$slate-tint;\n }\n\n &:focus-visible {\n outline: 2px solid colors.$accent-mid;\n outline-offset: 1px;\n }\n\n &.is-active {\n border-color: colors.$slate-tint;\n background-color: colors.$white;\n }\n\n &__icon {\n flex: none;\n font-size: i.icon-size(20);\n line-height: 1;\n }\n\n &__label {\n overflow: hidden;\n max-width: 110px;\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n\n // Collapsed rail: keep the label in the DOM for assistive tech, hide visually.\n &.is-collapsed .ds-menu-item__label {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n white-space: nowrap;\n }\n}\n</style>\n","// DsWizardStepper — numbered multi-step flow (step chips + next/final button).\n// 1:1 with the DS \"WizardStepper\" (components/navigation/DsWizardStepper.jsx).\n\nexport interface DsWizardStep {\n // Stable key for v-for; falls back to the 1-based index.\n key?: string | number;\n // Visible step label (consumer-owned text — the library never hardcodes copy).\n label: string;\n // Force the \"completed\" look regardless of position.\n completed?: boolean;\n // Visited but left unfinished → the red \"!\" chip.\n visited?: boolean;\n}\n\n// Derived per-step visual state (class: ds-wizard-step--<state>).\nexport const DS_WIZARD_STEP_STATES = {\n ACTIVE: 'active', // current step — accent-tint chip, filled marker\n COMPLETED: 'completed', // done — filled accent marker\n INCOMPLETE: 'incomplete', // visited but unfinished — red \"!\" chip\n NEUTRAL: 'neutral', // upcoming — outlined marker\n} as const;\nexport type DsWizardStepState = (typeof DS_WIZARD_STEP_STATES)[keyof typeof DS_WIZARD_STEP_STATES];\n","<template>\n <div class=\"ds-wizard-stepper\">\n <ol class=\"ds-wizard-stepper__steps\">\n <li v-for=\"step in resolvedSteps\" :key=\"step.key\" class=\"ds-wizard-stepper__item\">\n <component\n :is=\"clickable ? 'button' : 'div'\"\n :type=\"clickable ? 'button' : undefined\"\n class=\"ds-wizard-step\"\n :class=\"[`ds-wizard-step--${step.state}`, { 'is-clickable': clickable }]\"\n :aria-current=\"step.current ? 'step' : undefined\"\n @click=\"selectStep(step.index)\"\n >\n <span class=\"ds-wizard-step__marker\" aria-hidden=\"true\">{{ step.marker }}</span>\n <span class=\"ds-wizard-step__label\">{{ step.label }}</span>\n </component>\n </li>\n </ol>\n\n <!-- Acción: reusa DsButton (primary/large). Cada botón se gatea por su label:\n sin copy no hay CTA (evita un botón visible sin texto). Dos v-if\n independientes y mutuamente excluyentes (isLast) — no un v-else-if, que\n mostraría el botón final en un paso no-final cuando falta nextLabel. -->\n <DsButton\n v-if=\"!isLast && nextLabel\"\n variant=\"primary\"\n size=\"large\"\n right-icon=\"keyboard_arrow_right\"\n @click=\"next\"\n >\n {{ nextLabel }}\n </DsButton>\n <DsButton v-if=\"isLast && finalStepLabel\" variant=\"primary\" size=\"large\" @click=\"finish\">\n {{ finalStepLabel }}\n </DsButton>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, type PropType } from 'vue';\nimport { DsButton } from '@shared/ui/DsButton';\nimport { STEP, FINAL_STEP } from '@shared/constants/events';\nimport {\n DS_WIZARD_STEP_STATES,\n type DsWizardStep,\n type DsWizardStepState,\n} from './DsWizardStepper.types';\n\nconst props = defineProps({\n steps: { type: Array as PropType<DsWizardStep[]>, default: () => [] },\n // Current step (1-based).\n value: { type: Number, default: 1 },\n // Button labels — consumer-owned copy (no hardcoded text in the library).\n nextLabel: { type: String, default: '' },\n finalStepLabel: { type: String, default: '' },\n // Whether the step chips are interactive (advance buttons are always active).\n clickable: { type: Boolean, default: true },\n});\n\nconst emit = defineEmits([STEP, FINAL_STEP]);\n\nconst isLast = computed(() => props.value === props.steps.length);\n\nconst resolvedSteps = computed(() =>\n props.steps.map((step, i) => {\n const index = i + 1;\n // La precedencia importa: un `visited` explícito (paso dejado sin terminar)\n // tiene que ganarle al default posicional de \"ya pasó\". Si no, un paso\n // visited DETRÁS del actual (index < value) se resolvería como completed y\n // nunca mostraría el \"!\" rojo. Orden: completed explícito → INCOMPLETE\n // (visited) → default posicional.\n let state: DsWizardStepState;\n if (index === props.value) state = DS_WIZARD_STEP_STATES.ACTIVE;\n else if (step.completed) state = DS_WIZARD_STEP_STATES.COMPLETED;\n else if (step.visited) state = DS_WIZARD_STEP_STATES.INCOMPLETE;\n else if (index < props.value) state = DS_WIZARD_STEP_STATES.COMPLETED;\n else state = DS_WIZARD_STEP_STATES.NEUTRAL;\n return {\n key: step.key ?? index,\n index,\n label: step.label,\n state,\n current: state === DS_WIZARD_STEP_STATES.ACTIVE,\n marker: state === DS_WIZARD_STEP_STATES.INCOMPLETE ? '!' : String(index),\n };\n })\n);\n\n// Chip clicks are gated by `clickable`; the advance/final buttons never are.\nfunction selectStep(index: number) {\n if (!props.clickable) return;\n emit(STEP, index);\n}\n\n// Clamp: never emit a step past the last one. In normal use the button is\n// hidden on the last step, but guard against a desynced value > steps.length.\nfunction next() {\n if (props.value < props.steps.length) emit(STEP, props.value + 1);\n}\n\nfunction finish() {\n emit(FINAL_STEP);\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@use '../../assets/css/colors.scss' as colors;\n@use '../../assets/css/spacing.scss' as s;\n@use '../../assets/css/typography.scss' as t;\n@use '../../assets/css/radius.scss' as r;\n@use '../../assets/css/motion.scss' as m;\n\n.ds-wizard-stepper {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: s.spacing(2);\n\n &__steps {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: s.spacing(2);\n }\n\n &__item {\n display: flex;\n }\n}\n\n.ds-wizard-step {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: s.spacing(1);\n\n height: s.control-height('large');\n padding: s.spacing(1) s.spacing(1.5);\n\n color: colors.$accent-main;\n font-family: t.$font-family;\n font-weight: t.$weight-bold;\n font-size: t.font-size('body');\n white-space: nowrap;\n\n border: none;\n border-radius: r.$radius-control;\n background-color: transparent;\n\n transition: background-color m.$duration-default m.$easing;\n\n @include m.reduce-motion {\n transition: none;\n }\n\n &__marker {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: none;\n\n width: 20px;\n height: 20px;\n\n color: colors.$accent-main;\n font-size: t.font-size('caption');\n font-weight: t.$weight-bold;\n line-height: 1;\n\n border: 2px solid colors.$accent-main;\n border-radius: r.$radius-full;\n background-color: transparent;\n }\n\n &__label {\n line-height: 1;\n }\n\n /* ---- States (DS WizardStep) ---- */\n &--active {\n background-color: colors.$accent-tint;\n }\n &--active .ds-wizard-step__marker,\n &--completed .ds-wizard-step__marker {\n color: colors.$white;\n border: none;\n background-color: colors.$accent-main;\n }\n &--incomplete {\n color: colors.$error-main;\n }\n &--incomplete .ds-wizard-step__marker {\n color: colors.$error-main;\n border-color: colors.$error-main;\n }\n\n /* ---- Interactive affordance: hover + focus only when clickable ---- */\n &.is-clickable {\n cursor: pointer;\n\n &:focus-visible {\n outline: 2px solid colors.$accent-mid;\n outline-offset: 1px;\n }\n\n &.ds-wizard-step--neutral:hover,\n &.ds-wizard-step--completed:hover {\n background-color: colors.$accent-bg;\n }\n &.ds-wizard-step--incomplete:hover {\n background-color: colors.$error-mid;\n }\n // active keeps its accent-tint on hover (no override) — mirrors the DS.\n }\n}\n</style>\n"],"names":["DS_SPINNER_TONES","DS_SPINNER_SIZES","SPINNER_SIZE_PX","props","__props","resolvedSize","computed","_a","_createElementBlock","_normalizeClass","_normalizeStyle","CLICK","CLOSE","FOCUS","BLUR","CHANGE","FIELD_BLUR","COPY","CORRECT","INCORRECT","SORT","EDIT","SELECT_ALL","PAGE_CHANGE","PAGE_SIZE_CHANGE","SEARCH","SELECT","STEP","FINAL_STEP","NAVIGATE","DS_BUTTON_VARIANTS","DS_BUTTON_SIZES","DS_BUTTON_NATIVE_TYPES","emit","__emit","iconSize","spinnerTone","handleClick","event","_createBlock","_unref","DsSpinner","_createElementVNode","$slots","_openBlock","_hoisted_3","_renderSlot","_ctx","_hoisted_4","_toDisplayString","_hoisted_5","_hoisted_6","_hoisted_7","DS_TAG_TONES","_hoisted_1","_cache","_withModifiers","$event","DS_BADGE_TONES","initials","derivedLabel","ariaLabel","boxWidth","boxHeight","boxStyle","resolvedTextSize","hashed","seed","hash","i","rgb","light","_hoisted_2","model","_useModel","inputRef","ref","syncIndeterminate","onMounted","watch","DS_TABLE_ALIGN","DS_TABLE_PIN","compoundColspan","rowHeightStyle","heightStyle","onSelectAll","checked","pinWidthPx","width","match","columnPinStyles","map","left","col","right","pinEdgeKeys","lefts","c","rights","set","columnPinClass","column","columnHeaderStyle","pin","columnCellStyle","normalizedHeaders","h","headerPinStyles","list","headerPinEdges","headerPinClass","header","index","headerStyle","wrapperRef","scrolledStart","scrolledEnd","resizeObserver","updateScrollShadow","el","teardownScroll","prev","onBeforeUnmount","nextTick","_createVNode","DsCheckbox","_Fragment","_renderList","_hoisted_8","_hoisted_10","row","DS_INPUT_TYPES","DS_INPUT_SIZES","inputEl","onFieldFocusOut","e","next","root","slots","useSlots","reveal","toggleReveal","isPassword","isSearch","resolvedType","hasRevealToggle","hasSuffix","__expose","n","DS_TEXTAREA_RESIZE","attrs","useAttrs","isRootAttr","k","rootAttrs","controlAttrs","_mergeProps","DS_ALERT_TYPES","DS_ALERT_EFFECTS","DS_ALERT_ICONS","visible","icon","hasBody","hasTitle","onClose","_Transition","DS_NOTIFICATION_TYPES","DS_NOTIFICATION_POSITIONS","DS_NOTIFICATION_ICONS","DS_NOTIFICATION_LINK_TARGETS","linkRel","timer","startedAt","remaining","clearTimer","schedule","ms","pause","resume","onLinkClick","_Teleport","dialogRef","lastFocused","titleId","useId","dialogStyle","onBackdrop","focusables","onKeydown","f","first","last","activateModal","_b","deactivateModal","restoreFocus","active","wasActive","DsButton","DS_TOOLTIP_PLACEMENTS","tooltipId","hasContent","d","hide","hovering","focused","describedEl","prevDescribedBy","describe","undescribe","scheduleShow","onMouseLeave","onFocusIn","onFocusOut","wrapper","DS_VERIFICATION_STATES","status","_createTextVNode","copied","copiedTimer","handleCopy","handleCorrect","handleIncorrect","DsVerificationIcon","DsBadge","collapsed","total","showSections","empty","isCollapsed","toggle","onCopy","value","onSort","onEdit","onCorrect","datum","onIncorrect","_hoisted_9","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","DsContactValue","_hoisted_19","_hoisted_20","_hoisted_21","handleChange","DS_SELECT_TYPES","DS_SELECT_WIDTHS","instanceId","listboxId","optionId","rootRef","triggerRef","searchRef","open","activeIndex","query","isFilter","localFilterActive","filteredOptions","q","o","visibleOptions","runs","result","option","g","selectedValues","hasSelection","isFilterSelected","isSelected","labelCache","reactive","opts","labelFor","_c","summaryText","NAMED_WIDTHS","widthClass","rootStyle","activeOptionId","findInitialActiveIndex","firstSelected","openDropdown","closeDropdown","refocusTrigger","toggleOpen","selectOption","current","v","alreadySelected","clearSelection","moveActive","delta","len","onTriggerKeydown","onSearchKeydown","onSearchInput","onDocumentMousedown","isOpen","isDisabled","run","it","onClick","toLength","cellStyle","style","offset","DS_CUSTOM_TAG_TYPES","legendSegments","seg","hasLegend","idx","$attrs","ELLIPSIS_LEFT","ELLIPSIS_RIGHT","pageCount","pages","count","_","items","start","end","page","goTo","onSizeChange","target","item","s","DS_WIZARD_STEP_STATES","isLast","resolvedSteps","step","state","selectStep","finish","_resolveDynamicComponent"],"mappings":"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"}
|