@ds-mo/ui 14.2.0 → 14.3.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.
Files changed (123) hide show
  1. package/dist/.package-ready.json +1 -1
  2. package/dist/agent-patterns.json +1 -1
  3. package/dist/agent.json +144 -19
  4. package/dist/angular/ds-pagination.d.ts +19 -0
  5. package/dist/angular/ds-pagination.js +42 -0
  6. package/dist/angular/ds-table.d.ts +4 -1
  7. package/dist/angular/ds-table.js +7 -4
  8. package/dist/angular/index.d.ts +1 -1
  9. package/dist/angular/index.js +1 -0
  10. package/dist/angular/proxies.d.ts +1 -0
  11. package/dist/angular/proxies.js +1 -0
  12. package/dist/components/ds-agent-questionnaire.js +1 -1
  13. package/dist/components/ds-agent-response.js +1 -1
  14. package/dist/components/ds-banner.js +1 -1
  15. package/dist/components/ds-bar-nav.js +1 -1
  16. package/dist/components/ds-bar-title.js +1 -1
  17. package/dist/components/ds-bar-workflow.js +1 -1
  18. package/dist/components/ds-bar-workflow.js.map +1 -1
  19. package/dist/components/ds-card-overview.js +1 -1
  20. package/dist/components/ds-checkbox.js +1 -1
  21. package/dist/components/ds-code-block.js +1 -1
  22. package/dist/components/ds-filter-menu.js +1 -1
  23. package/dist/components/ds-input.js +1 -1
  24. package/dist/components/ds-markdown.js +1 -1
  25. package/dist/components/ds-menu.js +1 -1
  26. package/dist/components/ds-message-actions.js +1 -1
  27. package/dist/components/ds-message-composer.js +1 -1
  28. package/dist/components/ds-message-scroller.js +1 -1
  29. package/dist/components/ds-mobile-header.js +1 -1
  30. package/dist/components/ds-mobile-section-switcher.js +1 -1
  31. package/dist/components/ds-mobile-sheet-nav.js +1 -1
  32. package/dist/components/ds-modal.js +1 -1
  33. package/dist/components/ds-pagination.d.ts +11 -0
  34. package/dist/components/ds-pagination.js +2 -0
  35. package/dist/components/ds-pagination.js.map +1 -0
  36. package/dist/components/ds-panel-nav.js +1 -1
  37. package/dist/components/ds-panel-tool-header.js +1 -1
  38. package/dist/components/ds-panel-tool-search.js +1 -1
  39. package/dist/components/ds-panel-tools.js +1 -1
  40. package/dist/components/ds-radio.js +1 -1
  41. package/dist/components/ds-select.js +1 -1
  42. package/dist/components/ds-select.js.map +1 -1
  43. package/dist/components/ds-shell-app.js +1 -1
  44. package/dist/components/ds-shell-page.js +1 -1
  45. package/dist/components/ds-shell-tools.js +1 -1
  46. package/dist/components/ds-slider.js +1 -1
  47. package/dist/components/ds-swatch-picker.js +1 -1
  48. package/dist/components/ds-switch.js +1 -1
  49. package/dist/components/ds-table.js +1 -1
  50. package/dist/components/ds-table.js.map +1 -1
  51. package/dist/components/ds-toast.js +1 -1
  52. package/dist/components/ds-tooltip.js +1 -1
  53. package/dist/components/ds-typing-indicator.js +1 -1
  54. package/dist/components/index.d.ts +7 -0
  55. package/dist/components/{p-D2xWcVv6.js → p-B8q-6a03.js} +2 -2
  56. package/dist/components/{p-DYzjeIAT.js → p-BHjw0x3L.js} +2 -2
  57. package/dist/components/{p-CTVMXN9s.js → p-BSBQ5kGe.js} +2 -2
  58. package/dist/components/{p-DmZupC1-.js → p-BiBXCjjo.js} +2 -2
  59. package/dist/components/{p-BOCrvs-v.js → p-Btxwp4lJ.js} +2 -2
  60. package/dist/components/{p-DmwMiaWL.js → p-Bxe6532y.js} +2 -2
  61. package/dist/components/{p-DD5OCVXx.js → p-C5TrNd31.js} +2 -2
  62. package/dist/components/{p-ChlRsQYY.js → p-C68-8tlp.js} +2 -2
  63. package/dist/components/{p-BnPcWBA8.js → p-CW3sRYuf.js} +2 -2
  64. package/dist/components/{p-DJ_FsLCc.js → p-CWsziQQY.js} +2 -2
  65. package/dist/components/{p-CRjpqqAI.js → p-Cjf54_Cq.js} +2 -2
  66. package/dist/components/{p-NTU5jrG_.js → p-CkWVI16a.js} +2 -2
  67. package/dist/components/{p-BDCrM8-1.js → p-Cpqd69kg.js} +2 -2
  68. package/dist/components/p-CtOK5oZ1.js +2 -0
  69. package/dist/components/p-CtOK5oZ1.js.map +1 -0
  70. package/dist/components/p-D1ZI_10t.js +2 -0
  71. package/dist/components/p-D1ZI_10t.js.map +1 -0
  72. package/dist/components/{p-C-GNvCk0.js → p-Dh_TVLnz.js} +2 -2
  73. package/dist/components/{p-BiOfbCke.js → p-DkPNCBA8.js} +2 -2
  74. package/dist/components/{p-DT4WdAup.js → p-Dkkid-rh.js} +2 -2
  75. package/dist/components/p-DwqUW4Rg.js +2 -0
  76. package/dist/components/p-DwqUW4Rg.js.map +1 -0
  77. package/dist/components/{p-eKARJmWs.js → p-nImbYpwo.js} +2 -2
  78. package/dist/components/{p-BMJcBo8B.js → p-ohWEVGHg.js} +2 -2
  79. package/dist/components/{p-c9QiNOc7.js → p-qA4eKZkW.js} +2 -2
  80. package/dist/docs/components.json +470 -34
  81. package/dist/mcp-data/registry/pagination.json +295 -0
  82. package/dist/mcp-data/registry/registry.json +146 -18
  83. package/dist/mcp-data/registry/table.json +69 -37
  84. package/dist/mcp-data/registry/tooltip.json +2 -1
  85. package/dist/react/components.d.ts +1 -0
  86. package/dist/react/components.js +1 -0
  87. package/dist/react/ds-pagination.d.ts +12 -0
  88. package/dist/react/ds-pagination.js +12 -0
  89. package/dist/react/ds-table.d.ts +2 -1
  90. package/dist/react/ds-table.js +1 -0
  91. package/dist/styles/table.css +9 -3
  92. package/dist/types/components/Pagination/Pagination.d.ts +28 -0
  93. package/dist/types/components/Pagination/index.d.ts +3 -0
  94. package/dist/types/components/Pagination/pagination-model.d.ts +13 -0
  95. package/dist/types/components/Pagination/pagination-types.d.ts +25 -0
  96. package/dist/types/components/Table/Table.d.ts +10 -3
  97. package/dist/types/components/Table/index.d.ts +1 -1
  98. package/dist/types/components/Table/table-render-model.d.ts +1 -1
  99. package/dist/types/components/Table/table-types.d.ts +5 -1
  100. package/dist/types/components/Tooltip/Tooltip.d.ts +3 -0
  101. package/dist/types/components.d.ts +148 -13
  102. package/package.json +1 -1
  103. package/dist/components/p-CtSxm6PI.js +0 -2
  104. package/dist/components/p-CtSxm6PI.js.map +0 -1
  105. /package/dist/components/{p-D2xWcVv6.js.map → p-B8q-6a03.js.map} +0 -0
  106. /package/dist/components/{p-DYzjeIAT.js.map → p-BHjw0x3L.js.map} +0 -0
  107. /package/dist/components/{p-CTVMXN9s.js.map → p-BSBQ5kGe.js.map} +0 -0
  108. /package/dist/components/{p-DmZupC1-.js.map → p-BiBXCjjo.js.map} +0 -0
  109. /package/dist/components/{p-BOCrvs-v.js.map → p-Btxwp4lJ.js.map} +0 -0
  110. /package/dist/components/{p-DmwMiaWL.js.map → p-Bxe6532y.js.map} +0 -0
  111. /package/dist/components/{p-DD5OCVXx.js.map → p-C5TrNd31.js.map} +0 -0
  112. /package/dist/components/{p-ChlRsQYY.js.map → p-C68-8tlp.js.map} +0 -0
  113. /package/dist/components/{p-BnPcWBA8.js.map → p-CW3sRYuf.js.map} +0 -0
  114. /package/dist/components/{p-DJ_FsLCc.js.map → p-CWsziQQY.js.map} +0 -0
  115. /package/dist/components/{p-CRjpqqAI.js.map → p-Cjf54_Cq.js.map} +0 -0
  116. /package/dist/components/{p-NTU5jrG_.js.map → p-CkWVI16a.js.map} +0 -0
  117. /package/dist/components/{p-BDCrM8-1.js.map → p-Cpqd69kg.js.map} +0 -0
  118. /package/dist/components/{p-C-GNvCk0.js.map → p-Dh_TVLnz.js.map} +0 -0
  119. /package/dist/components/{p-BiOfbCke.js.map → p-DkPNCBA8.js.map} +0 -0
  120. /package/dist/components/{p-DT4WdAup.js.map → p-Dkkid-rh.js.map} +0 -0
  121. /package/dist/components/{p-eKARJmWs.js.map → p-nImbYpwo.js.map} +0 -0
  122. /package/dist/components/{p-BMJcBo8B.js.map → p-ohWEVGHg.js.map} +0 -0
  123. /package/dist/components/{p-c9QiNOc7.js.map → p-qA4eKZkW.js.map} +0 -0
@@ -57,6 +57,7 @@
57
57
  "The first hover or keyboard focus waits for delay; moving to another tooltip shortly after dismissal opens it immediately.",
58
58
  "An empty or whitespace-only label suppresses the popup while preserving the wrapper.",
59
59
  "Only one tooltip remains active globally; warm handoff removes the previous popup before showing the next.",
60
+ "Disconnecting and reinserting the tooltip host rebinds the current slotted trigger.",
60
61
  "The preferred side flips when it cannot fit, then alignment is clamped to viewport padding.",
61
62
  "Reduced motion removes enter and exit fades while retaining the final visible or hidden state."
62
63
  ],
@@ -299,7 +300,7 @@
299
300
  },
300
301
  {
301
302
  "path": "src/wc/components/Tooltip/Tooltip.tsx",
302
- "content": "import { Component, Prop, Element, Watch, h, Host } from '@stencil/core';\nimport {\n CONTROL_TEXT_VARIANT,\n resolveCssLengthPx,\n resolveCssTimeMs,\n resolveMotionTimeMs,\n TOKEN_CSS_LENGTHS,\n TOKEN_DEFAULTS,\n} from '../../utils';\nimport type { TextVariant } from '../Text/text-types';\nimport { shortcutKeyLabels } from '../../utils/shortcut-key';\nimport { AnchoredPositionController } from '../../utils/anchored-position-controller';\n// Side-effect: register `ds-text` — the portal builds it via createElement, not JSX.\nimport '../Text/Text';\n\nexport type TooltipSide = 'top' | 'right' | 'bottom' | 'left';\nexport type TooltipAlign = 'start' | 'center' | 'end';\nexport type TooltipSize = 'md' | 'sm' | 'xs';\n\nconst FALLBACK_HEIGHT: Record<TooltipSize, string> = {\n md: TOKEN_DEFAULTS.size400,\n sm: TOKEN_DEFAULTS.size300,\n xs: TOKEN_DEFAULTS.size200,\n};\n\nlet lastDismissedAt = 0;\nlet tooltipIdCounter = 0;\n/** Currently open tooltip — force-cleared on warm handoff so tips don't cross-fade. */\nlet activeTooltip: Tooltip | null = null;\n\ntype DsTextEl = HTMLElement & {\n as: string;\n variant: TextVariant;\n emphasis: boolean;\n color: string;\n wrap: string;\n};\n\nfunction createDsText(opts: {\n variant: TextVariant;\n emphasis?: boolean;\n color?: string;\n wrap?: 'wrap' | 'nowrap' | 'balance' | 'pretty';\n text: string;\n}): DsTextEl {\n const el = document.createElement('ds-text') as DsTextEl;\n el.as = 'span';\n el.variant = opts.variant;\n el.emphasis = opts.emphasis ?? false;\n el.color = opts.color ?? 'inherit';\n if (opts.wrap) el.wrap = opts.wrap;\n el.textContent = opts.text;\n return el;\n}\n\n/**\n * Control-density label styles for portaled ds-text.\n * Scoped CSS requires `sc-ds-tooltip` on the host, but Text's `<Host class>`\n * overwrites imperative className — so these must be inline to stick.\n */\nfunction styleTooltipLabel(el: HTMLElement) {\n el.style.flexShrink = '0';\n el.style.minWidth = 'max-content';\n el.style.maxWidth = 'none';\n el.style.padding = '0 var(--ds-control-label-inset, var(--dimension-space-025))';\n el.style.color = 'var(--color-foreground-primary)';\n}\n\n/**\n * Imperative body portal for the popup.\n * Stencil must not own the portaled node — moving a VDOM child to `document.body`\n * causes recreate → re-portal → @State loops that freeze the page.\n */\n@Component({\n tag: 'ds-tooltip',\n styleUrl: 'Tooltip.css',\n scoped: true,\n})\nexport class Tooltip {\n @Element() el!: HTMLElement;\n\n /**\n * Tooltip text. Empty/whitespace skips show — useful when the wrapper must stay\n * mounted for layout/animation stability (e.g. PanelNav expand/collapse).\n */\n @Prop() label!: string;\n\n /** Control density (height, padding, type). */\n @Prop() size: TooltipSize = 'md';\n\n @Prop() side: TooltipSide = 'top';\n @Prop() align: TooltipAlign = 'center';\n /** Gap between anchor and tooltip — number (px) or TokoMo length. */\n @Prop() sideOffset: number | string = TOKEN_CSS_LENGTHS.space050;\n /** Cross-axis offset — number (px) or TokoMo length. */\n @Prop() alignOffset: number | string = 0;\n /**\n * Hover show delay before the tooltip appears.\n * Default: `--effect-animation-delay-medium-3` (1000ms). Accepts a number (ms)\n * or a TokoMo time token / `var(--effect-animation-delay-*)`. Applies to the\n * initial hover/focus show only — after a recent dismiss, reopen is instant.\n * Prefer the default; override only for denser chrome or rare/destructive actions.\n */\n @Prop() delay: number | string = TOKEN_DEFAULTS.animationDelayMedium3;\n @Prop() shortcutKey: string | undefined;\n @Prop() shortcutKeyPosition: 'start' | 'end' = 'end';\n\n private tooltipId = `ds-tooltip-${++tooltipIdCounter}`;\n private delayTimer: ReturnType<typeof setTimeout> | null = null;\n private closeTimer: ReturnType<typeof setTimeout> | null = null;\n private anchor: HTMLElement | null = null;\n private popupEl: HTMLElement | null = null;\n private anchorObserver: MutationObserver | null = null;\n private readonly position = new AnchoredPositionController({\n getAnchor: () => this.anchor,\n getPopup: () => this.popupEl,\n measure: (anchor, tip) => ({\n anchorRect: anchor.getBoundingClientRect(),\n // Fall back to token metrics until `ds-text` upgrades and reports a width.\n popupWidth: tip.offsetWidth || this.tooltipFallbackWidthPx,\n popupHeight: tip.offsetHeight || this.tooltipFallbackHeightPx,\n side: this.side,\n align: this.align,\n sideOffsetPx: this.sideOffsetPx,\n alignOffsetPx: this.alignOffsetPx,\n viewportPadPx: this.viewportPadPx,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n }),\n apply: ({ x, y, resolvedSide }) => {\n if (!this.popupEl) return;\n this.popupEl.dataset.side = resolvedSide;\n this.popupEl.style.transform = `translate(${Math.round(x)}px, ${Math.round(y)}px)`;\n },\n liveUpdate: 'frame',\n observeResize: true,\n });\n private skipEnterAnimation = false;\n private isOpen = false;\n private lastInteractionWasKeyboard = false;\n private pointerEnterHandler = (event: PointerEvent) => {\n if (event.pointerType === 'touch') return;\n this.show();\n };\n private pointerLeaveHandler = (event: PointerEvent) => {\n if (event.pointerType === 'touch') return;\n this.hide();\n };\n private pointerDownHandler = (event: PointerEvent) => {\n this.lastInteractionWasKeyboard = false;\n if (event.pointerType === 'touch' && (this.isOpen || this.popupEl)) this.hideInstant();\n };\n private keyDownHandler = () => {\n this.lastInteractionWasKeyboard = true;\n };\n /** focusin/focusout so nested focus targets (e.g. ds-button-* inner button) still open the tip. */\n private focusHandler = () => {\n if (this.lastInteractionWasKeyboard || this.anchor?.matches(':focus-visible')) this.show();\n };\n private blurHandler: (e: FocusEvent) => void = (e) => {\n const next = e.relatedTarget as Node | null;\n if (next && this.anchor?.contains(next)) return;\n this.hide();\n };\n private escapeHandler = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || !this.isOpen) return;\n event.preventDefault();\n this.hide();\n };\n\n componentDidLoad() {\n this.bindAnchor();\n this.anchorObserver = new MutationObserver(() => this.handleSlotChange());\n this.anchorObserver.observe(this.el, { childList: true });\n }\n\n disconnectedCallback() {\n if (activeTooltip === this) activeTooltip = null;\n this.anchorObserver?.disconnect();\n this.anchorObserver = null;\n this.unbindAnchor();\n this.clearTimers();\n this.destroyPopup();\n }\n\n @Watch('label')\n @Watch('size')\n @Watch('side')\n @Watch('align')\n @Watch('sideOffset')\n @Watch('alignOffset')\n @Watch('shortcutKey')\n @Watch('shortcutKeyPosition')\n onContentOrPositionChange() {\n if (!this.label?.trim()) {\n if (this.isOpen || this.popupEl) this.hideInstant();\n return;\n }\n if (!this.popupEl || !this.isOpen) return;\n this.renderPopupContent();\n this.schedulePosition();\n }\n\n private get viewportPadPx(): number {\n return resolveCssLengthPx(TOKEN_DEFAULTS.space050, TOKEN_DEFAULTS.space050);\n }\n\n private get sideOffsetPx(): number {\n return resolveCssLengthPx(this.sideOffset, TOKEN_DEFAULTS.space050);\n }\n\n private get alignOffsetPx(): number {\n return resolveCssLengthPx(this.alignOffset, 0);\n }\n\n private get hoverDelayMs(): number {\n return resolveCssTimeMs(this.delay, TOKEN_DEFAULTS.animationDelayMedium3);\n }\n\n private get instantReopenMs(): number {\n return resolveCssTimeMs(\n TOKEN_DEFAULTS.animationDurationMedium1,\n TOKEN_DEFAULTS.animationDurationMedium1,\n );\n }\n\n private get fadeOutMs(): number {\n return resolveMotionTimeMs(TOKEN_DEFAULTS.motionShort3, TOKEN_DEFAULTS.animationDurationShort3);\n }\n\n private get tooltipFallbackWidthPx(): number {\n return resolveCssLengthPx(TOKEN_DEFAULTS.tooltipFallbackWidth, TOKEN_DEFAULTS.tooltipFallbackWidth);\n }\n\n private get tooltipFallbackHeightPx(): number {\n const token = FALLBACK_HEIGHT[this.size];\n return resolveCssLengthPx(token, token);\n }\n\n private bindAnchor() {\n const slot = this.el.querySelector('slot') as HTMLSlotElement | null;\n const slotted = slot?.assignedElements?.() ?? [];\n const child = (slotted[0] ?? this.el.firstElementChild) as HTMLElement | null;\n if (!child || child === this.anchor) return;\n this.anchor = child;\n child.addEventListener('pointerenter', this.pointerEnterHandler);\n child.addEventListener('pointerleave', this.pointerLeaveHandler);\n child.addEventListener('pointerdown', this.pointerDownHandler);\n child.addEventListener('keydown', this.keyDownHandler);\n child.addEventListener('focusin', this.focusHandler);\n child.addEventListener('focusout', this.blurHandler);\n }\n\n private unbindAnchor() {\n if (!this.anchor) return;\n this.unlinkDescribedBy();\n this.anchor.removeEventListener('pointerenter', this.pointerEnterHandler);\n this.anchor.removeEventListener('pointerleave', this.pointerLeaveHandler);\n this.anchor.removeEventListener('pointerdown', this.pointerDownHandler);\n this.anchor.removeEventListener('keydown', this.keyDownHandler);\n this.anchor.removeEventListener('focusin', this.focusHandler);\n this.anchor.removeEventListener('focusout', this.blurHandler);\n this.anchor = null;\n }\n\n private linkDescribedBy() {\n if (!this.anchor || !this.popupEl) return;\n const ids = new Set((this.anchor.getAttribute('aria-describedby') ?? '').split(/\\s+/).filter(Boolean));\n ids.add(this.tooltipId);\n this.anchor.setAttribute('aria-describedby', Array.from(ids).join(' '));\n }\n\n private unlinkDescribedBy() {\n if (!this.anchor) return;\n const ids = (this.anchor.getAttribute('aria-describedby') ?? '')\n .split(/\\s+/)\n .filter(id => id && id !== this.tooltipId);\n if (ids.length) this.anchor.setAttribute('aria-describedby', ids.join(' '));\n else this.anchor.removeAttribute('aria-describedby');\n }\n\n private handleSlotChange = () => {\n const slot = this.el.querySelector('slot') as HTMLSlotElement | null;\n const next = (slot?.assignedElements?.()[0] ?? this.el.firstElementChild) as HTMLElement | null;\n if (next === this.anchor) return;\n if (this.isOpen || this.popupEl) this.hideInstant();\n this.unbindAnchor();\n this.bindAnchor();\n };\n\n private clearTimers() {\n if (this.delayTimer) { clearTimeout(this.delayTimer); this.delayTimer = null; }\n if (this.closeTimer) { clearTimeout(this.closeTimer); this.closeTimer = null; }\n }\n\n private setupOpenListeners() {\n // Binds scroll/resize + the resize observer; placement itself is scheduled\n // by schedulePosition() so the label restyle stays interleaved.\n this.position.observe();\n document.addEventListener('keydown', this.escapeHandler);\n }\n\n private teardownOpenListeners() {\n this.position.unobserve();\n document.removeEventListener('keydown', this.escapeHandler);\n }\n\n private destroyPopup() {\n this.teardownOpenListeners();\n this.unlinkDescribedBy();\n if (!this.popupEl) return;\n this.popupEl.remove();\n this.popupEl = null;\n }\n\n /**\n * Position after `ds-text` upgrades and lays out. Measuring synchronously in\n * `mountPopup` reads a collapsed/fallback width and places left/right tips wrong.\n */\n private schedulePosition() {\n if (!this.popupEl) return;\n\n const reapplyLabelStyles = () => {\n const label = this.popupEl?.querySelector(':scope > .tooltip-inner > ds-text');\n if (label instanceof HTMLElement) styleTooltipLabel(label);\n };\n\n requestAnimationFrame(() => {\n reapplyLabelStyles();\n this.position.update();\n requestAnimationFrame(() => {\n reapplyLabelStyles();\n this.position.update();\n });\n });\n\n // The popup remounts on every show, so re-target the observer bound in start().\n this.position.observeResizeTargets();\n }\n\n /** Drop the open tip immediately (warm handoff to another tooltip). */\n private hideInstant() {\n this.clearTimers();\n lastDismissedAt = Date.now();\n if (activeTooltip === this) activeTooltip = null;\n this.destroyPopup();\n this.isOpen = false;\n this.skipEnterAnimation = false;\n }\n\n private show() {\n // Empty label = wrapper present for DOM stability (e.g. PanelNav expand/collapse)\n // but no tip should appear.\n if (!this.label?.trim()) return;\n this.clearTimers();\n let instant = Date.now() - lastDismissedAt < this.instantReopenMs;\n if (activeTooltip && activeTooltip !== this) {\n activeTooltip.hideInstant();\n instant = true;\n }\n // eslint-disable-next-line @typescript-eslint/no-this-alias -- module singleton tracks the active instance\n activeTooltip = this;\n this.skipEnterAnimation = instant;\n if (instant) {\n this.mountPopup();\n return;\n }\n this.delayTimer = setTimeout(() => {\n this.delayTimer = null;\n if (!this.label?.trim()) return;\n this.mountPopup();\n }, this.hoverDelayMs);\n }\n\n private hide() {\n this.clearTimers();\n if (!this.popupEl && !this.isOpen) return;\n lastDismissedAt = Date.now();\n if (!this.popupEl) {\n this.isOpen = false;\n return;\n }\n this.popupEl.classList.remove('tooltip-popup--instant');\n this.popupEl.classList.add('tooltip-popup--closing');\n if (this.fadeOutMs === 0) {\n if (activeTooltip === this) activeTooltip = null;\n this.destroyPopup();\n this.isOpen = false;\n this.skipEnterAnimation = false;\n return;\n }\n this.closeTimer = setTimeout(() => {\n this.closeTimer = null;\n if (activeTooltip === this) activeTooltip = null;\n this.destroyPopup();\n this.isOpen = false;\n this.skipEnterAnimation = false;\n }, this.fadeOutMs);\n }\n\n private mountPopup() {\n if (!this.popupEl) {\n const tip = document.createElement('div');\n tip.id = this.tooltipId;\n tip.setAttribute('role', 'tooltip');\n // `sc-ds-tooltip` is required for Stencil scoped CSS to match outside the host.\n tip.className = 'tooltip-popup ds-forced-colors-scope sc-ds-tooltip';\n Object.assign(tip.style, {\n position: 'fixed',\n left: '0',\n top: '0',\n zIndex: 'var(--dimension-z-index-tooltip)',\n pointerEvents: 'none',\n });\n document.body.appendChild(tip);\n this.popupEl = tip;\n }\n\n this.setupOpenListeners();\n this.linkDescribedBy();\n this.popupEl.classList.toggle('tooltip-popup--instant', this.skipEnterAnimation);\n this.popupEl.classList.remove('tooltip-popup--closing');\n this.renderPopupContent();\n this.schedulePosition();\n this.isOpen = true;\n }\n\n private renderPopupContent() {\n if (!this.popupEl) return;\n const textVariant = CONTROL_TEXT_VARIANT[this.size];\n const density =\n this.size === 'md' ? 'ds-control--md' : this.size === 'sm' ? 'ds-control--sm' : 'ds-control--xs';\n // Every layout node needs `sc-ds-tooltip` so scoped selectors (`.foo.sc-ds-tooltip`) match.\n const sc = 'sc-ds-tooltip';\n\n const inner = document.createElement('div');\n inner.className = `tooltip-inner ${density} ${sc}`;\n\n const appendShortcut = (position: 'start' | 'end') => {\n if (!this.shortcutKey || this.shortcutKeyPosition !== position) return;\n const group = document.createElement('span');\n group.className = `key-hint-group ${sc}`;\n group.setAttribute('aria-hidden', 'true');\n\n for (const label of shortcutKeyLabels(this.shortcutKey)) {\n const hint = document.createElement('span');\n const wide = Array.from(label).length > 1 ? ' key-hint--wide' : '';\n hint.className = `key-hint${wide} ${sc}`;\n const text = document.createElement('span');\n text.className = `key-hint__label ${sc}`;\n text.textContent = label;\n hint.appendChild(text);\n group.appendChild(hint);\n }\n inner.appendChild(group);\n };\n\n appendShortcut('start');\n\n // ds-text is the layout box (metric-box + 2px control label-inset).\n // Inline styles required — see styleTooltipLabel.\n const label = createDsText({ variant: textVariant, wrap: 'nowrap', text: this.label });\n styleTooltipLabel(label);\n inner.appendChild(label);\n\n appendShortcut('end');\n\n this.popupEl.replaceChildren(inner);\n }\n\n render() {\n return (\n <Host style={{ display: 'contents' }}>\n <slot onSlotchange={this.handleSlotChange} />\n </Host>\n );\n }\n}\n",
303
+ "content": "import { Component, Prop, Element, Watch, h, Host } from '@stencil/core';\nimport {\n CONTROL_TEXT_VARIANT,\n resolveCssLengthPx,\n resolveCssTimeMs,\n resolveMotionTimeMs,\n TOKEN_CSS_LENGTHS,\n TOKEN_DEFAULTS,\n} from '../../utils';\nimport type { TextVariant } from '../Text/text-types';\nimport { shortcutKeyLabels } from '../../utils/shortcut-key';\nimport { AnchoredPositionController } from '../../utils/anchored-position-controller';\n// Side-effect: register `ds-text` — the portal builds it via createElement, not JSX.\nimport '../Text/Text';\n\nexport type TooltipSide = 'top' | 'right' | 'bottom' | 'left';\nexport type TooltipAlign = 'start' | 'center' | 'end';\nexport type TooltipSize = 'md' | 'sm' | 'xs';\n\nconst FALLBACK_HEIGHT: Record<TooltipSize, string> = {\n md: TOKEN_DEFAULTS.size400,\n sm: TOKEN_DEFAULTS.size300,\n xs: TOKEN_DEFAULTS.size200,\n};\n\nlet lastDismissedAt = 0;\nlet tooltipIdCounter = 0;\n/** Currently open tooltip — force-cleared on warm handoff so tips don't cross-fade. */\nlet activeTooltip: Tooltip | null = null;\n\ntype DsTextEl = HTMLElement & {\n as: string;\n variant: TextVariant;\n emphasis: boolean;\n color: string;\n wrap: string;\n};\n\nfunction createDsText(opts: {\n variant: TextVariant;\n emphasis?: boolean;\n color?: string;\n wrap?: 'wrap' | 'nowrap' | 'balance' | 'pretty';\n text: string;\n}): DsTextEl {\n const el = document.createElement('ds-text') as DsTextEl;\n el.as = 'span';\n el.variant = opts.variant;\n el.emphasis = opts.emphasis ?? false;\n el.color = opts.color ?? 'inherit';\n if (opts.wrap) el.wrap = opts.wrap;\n el.textContent = opts.text;\n return el;\n}\n\n/**\n * Control-density label styles for portaled ds-text.\n * Scoped CSS requires `sc-ds-tooltip` on the host, but Text's `<Host class>`\n * overwrites imperative className — so these must be inline to stick.\n */\nfunction styleTooltipLabel(el: HTMLElement) {\n el.style.flexShrink = '0';\n el.style.minWidth = 'max-content';\n el.style.maxWidth = 'none';\n el.style.padding = '0 var(--ds-control-label-inset, var(--dimension-space-025))';\n el.style.color = 'var(--color-foreground-primary)';\n}\n\n/**\n * Imperative body portal for the popup.\n * Stencil must not own the portaled node — moving a VDOM child to `document.body`\n * causes recreate → re-portal → @State loops that freeze the page.\n */\n@Component({\n tag: 'ds-tooltip',\n styleUrl: 'Tooltip.css',\n scoped: true,\n})\nexport class Tooltip {\n @Element() el!: HTMLElement;\n\n /**\n * Tooltip text. Empty/whitespace skips show — useful when the wrapper must stay\n * mounted for layout/animation stability (e.g. PanelNav expand/collapse).\n */\n @Prop() label!: string;\n\n /** Control density (height, padding, type). */\n @Prop() size: TooltipSize = 'md';\n\n @Prop() side: TooltipSide = 'top';\n @Prop() align: TooltipAlign = 'center';\n /** Gap between anchor and tooltip — number (px) or TokoMo length. */\n @Prop() sideOffset: number | string = TOKEN_CSS_LENGTHS.space050;\n /** Cross-axis offset — number (px) or TokoMo length. */\n @Prop() alignOffset: number | string = 0;\n /**\n * Hover show delay before the tooltip appears.\n * Default: `--effect-animation-delay-medium-3` (1000ms). Accepts a number (ms)\n * or a TokoMo time token / `var(--effect-animation-delay-*)`. Applies to the\n * initial hover/focus show only — after a recent dismiss, reopen is instant.\n * Prefer the default; override only for denser chrome or rare/destructive actions.\n */\n @Prop() delay: number | string = TOKEN_DEFAULTS.animationDelayMedium3;\n @Prop() shortcutKey: string | undefined;\n @Prop() shortcutKeyPosition: 'start' | 'end' = 'end';\n\n private tooltipId = `ds-tooltip-${++tooltipIdCounter}`;\n private delayTimer: ReturnType<typeof setTimeout> | null = null;\n private closeTimer: ReturnType<typeof setTimeout> | null = null;\n private anchor: HTMLElement | null = null;\n private popupEl: HTMLElement | null = null;\n private anchorObserver: MutationObserver | null = null;\n private didLoad = false;\n private readonly position = new AnchoredPositionController({\n getAnchor: () => this.anchor,\n getPopup: () => this.popupEl,\n measure: (anchor, tip) => ({\n anchorRect: anchor.getBoundingClientRect(),\n // Fall back to token metrics until `ds-text` upgrades and reports a width.\n popupWidth: tip.offsetWidth || this.tooltipFallbackWidthPx,\n popupHeight: tip.offsetHeight || this.tooltipFallbackHeightPx,\n side: this.side,\n align: this.align,\n sideOffsetPx: this.sideOffsetPx,\n alignOffsetPx: this.alignOffsetPx,\n viewportPadPx: this.viewportPadPx,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n }),\n apply: ({ x, y, resolvedSide }) => {\n if (!this.popupEl) return;\n this.popupEl.dataset.side = resolvedSide;\n this.popupEl.style.transform = `translate(${Math.round(x)}px, ${Math.round(y)}px)`;\n },\n liveUpdate: 'frame',\n observeResize: true,\n });\n private skipEnterAnimation = false;\n private isOpen = false;\n private lastInteractionWasKeyboard = false;\n private pointerEnterHandler = (event: PointerEvent) => {\n if (event.pointerType === 'touch') return;\n this.show();\n };\n private pointerLeaveHandler = (event: PointerEvent) => {\n if (event.pointerType === 'touch') return;\n this.hide();\n };\n private pointerDownHandler = (event: PointerEvent) => {\n this.lastInteractionWasKeyboard = false;\n if (event.pointerType === 'touch' && (this.isOpen || this.popupEl)) this.hideInstant();\n };\n private keyDownHandler = () => {\n this.lastInteractionWasKeyboard = true;\n };\n /** focusin/focusout so nested focus targets (e.g. ds-button-* inner button) still open the tip. */\n private focusHandler = () => {\n if (this.lastInteractionWasKeyboard || this.anchor?.matches(':focus-visible')) this.show();\n };\n private blurHandler: (e: FocusEvent) => void = (e) => {\n const next = e.relatedTarget as Node | null;\n if (next && this.anchor?.contains(next)) return;\n this.hide();\n };\n private escapeHandler = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || !this.isOpen) return;\n event.preventDefault();\n this.hide();\n };\n\n connectedCallback() {\n if (!this.didLoad) return;\n this.connectAnchor();\n }\n\n componentDidLoad() {\n this.didLoad = true;\n this.connectAnchor();\n }\n\n private connectAnchor() {\n this.bindAnchor();\n if (this.anchorObserver) return;\n this.anchorObserver = new MutationObserver(() => this.handleSlotChange());\n this.anchorObserver.observe(this.el, { childList: true });\n }\n\n disconnectedCallback() {\n if (activeTooltip === this) activeTooltip = null;\n this.anchorObserver?.disconnect();\n this.anchorObserver = null;\n this.unbindAnchor();\n this.clearTimers();\n this.destroyPopup();\n }\n\n @Watch('label')\n @Watch('size')\n @Watch('side')\n @Watch('align')\n @Watch('sideOffset')\n @Watch('alignOffset')\n @Watch('shortcutKey')\n @Watch('shortcutKeyPosition')\n onContentOrPositionChange() {\n if (!this.label?.trim()) {\n if (this.isOpen || this.popupEl) this.hideInstant();\n return;\n }\n if (!this.popupEl || !this.isOpen) return;\n this.renderPopupContent();\n this.schedulePosition();\n }\n\n private get viewportPadPx(): number {\n return resolveCssLengthPx(TOKEN_DEFAULTS.space050, TOKEN_DEFAULTS.space050);\n }\n\n private get sideOffsetPx(): number {\n return resolveCssLengthPx(this.sideOffset, TOKEN_DEFAULTS.space050);\n }\n\n private get alignOffsetPx(): number {\n return resolveCssLengthPx(this.alignOffset, 0);\n }\n\n private get hoverDelayMs(): number {\n return resolveCssTimeMs(this.delay, TOKEN_DEFAULTS.animationDelayMedium3);\n }\n\n private get instantReopenMs(): number {\n return resolveCssTimeMs(\n TOKEN_DEFAULTS.animationDurationMedium1,\n TOKEN_DEFAULTS.animationDurationMedium1,\n );\n }\n\n private get fadeOutMs(): number {\n return resolveMotionTimeMs(TOKEN_DEFAULTS.motionShort3, TOKEN_DEFAULTS.animationDurationShort3);\n }\n\n private get tooltipFallbackWidthPx(): number {\n return resolveCssLengthPx(TOKEN_DEFAULTS.tooltipFallbackWidth, TOKEN_DEFAULTS.tooltipFallbackWidth);\n }\n\n private get tooltipFallbackHeightPx(): number {\n const token = FALLBACK_HEIGHT[this.size];\n return resolveCssLengthPx(token, token);\n }\n\n private bindAnchor() {\n const slot = this.el.querySelector('slot') as HTMLSlotElement | null;\n const slotted = slot?.assignedElements?.() ?? [];\n const child = (slotted[0] ?? this.el.firstElementChild) as HTMLElement | null;\n if (!child || child === this.anchor) return;\n this.anchor = child;\n child.addEventListener('pointerenter', this.pointerEnterHandler);\n child.addEventListener('pointerleave', this.pointerLeaveHandler);\n child.addEventListener('pointerdown', this.pointerDownHandler);\n child.addEventListener('keydown', this.keyDownHandler);\n child.addEventListener('focusin', this.focusHandler);\n child.addEventListener('focusout', this.blurHandler);\n }\n\n private unbindAnchor() {\n if (!this.anchor) return;\n this.unlinkDescribedBy();\n this.anchor.removeEventListener('pointerenter', this.pointerEnterHandler);\n this.anchor.removeEventListener('pointerleave', this.pointerLeaveHandler);\n this.anchor.removeEventListener('pointerdown', this.pointerDownHandler);\n this.anchor.removeEventListener('keydown', this.keyDownHandler);\n this.anchor.removeEventListener('focusin', this.focusHandler);\n this.anchor.removeEventListener('focusout', this.blurHandler);\n this.anchor = null;\n }\n\n private linkDescribedBy() {\n if (!this.anchor || !this.popupEl) return;\n const ids = new Set((this.anchor.getAttribute('aria-describedby') ?? '').split(/\\s+/).filter(Boolean));\n ids.add(this.tooltipId);\n this.anchor.setAttribute('aria-describedby', Array.from(ids).join(' '));\n }\n\n private unlinkDescribedBy() {\n if (!this.anchor) return;\n const ids = (this.anchor.getAttribute('aria-describedby') ?? '')\n .split(/\\s+/)\n .filter(id => id && id !== this.tooltipId);\n if (ids.length) this.anchor.setAttribute('aria-describedby', ids.join(' '));\n else this.anchor.removeAttribute('aria-describedby');\n }\n\n private handleSlotChange = () => {\n const slot = this.el.querySelector('slot') as HTMLSlotElement | null;\n const next = (slot?.assignedElements?.()[0] ?? this.el.firstElementChild) as HTMLElement | null;\n if (next === this.anchor) return;\n if (this.isOpen || this.popupEl) this.hideInstant();\n this.unbindAnchor();\n this.bindAnchor();\n };\n\n private clearTimers() {\n if (this.delayTimer) { clearTimeout(this.delayTimer); this.delayTimer = null; }\n if (this.closeTimer) { clearTimeout(this.closeTimer); this.closeTimer = null; }\n }\n\n private setupOpenListeners() {\n // Binds scroll/resize + the resize observer; placement itself is scheduled\n // by schedulePosition() so the label restyle stays interleaved.\n this.position.observe();\n document.addEventListener('keydown', this.escapeHandler);\n }\n\n private teardownOpenListeners() {\n this.position.unobserve();\n document.removeEventListener('keydown', this.escapeHandler);\n }\n\n private destroyPopup() {\n this.teardownOpenListeners();\n this.unlinkDescribedBy();\n if (!this.popupEl) return;\n this.popupEl.remove();\n this.popupEl = null;\n }\n\n /**\n * Position after `ds-text` upgrades and lays out. Measuring synchronously in\n * `mountPopup` reads a collapsed/fallback width and places left/right tips wrong.\n */\n private schedulePosition() {\n if (!this.popupEl) return;\n\n const reapplyLabelStyles = () => {\n const label = this.popupEl?.querySelector(':scope > .tooltip-inner > ds-text');\n if (label instanceof HTMLElement) styleTooltipLabel(label);\n };\n\n requestAnimationFrame(() => {\n reapplyLabelStyles();\n this.position.update();\n requestAnimationFrame(() => {\n reapplyLabelStyles();\n this.position.update();\n });\n });\n\n // The popup remounts on every show, so re-target the observer bound in start().\n this.position.observeResizeTargets();\n }\n\n /** Drop the open tip immediately (warm handoff to another tooltip). */\n private hideInstant() {\n this.clearTimers();\n lastDismissedAt = Date.now();\n if (activeTooltip === this) activeTooltip = null;\n this.destroyPopup();\n this.isOpen = false;\n this.skipEnterAnimation = false;\n }\n\n private show() {\n // Empty label = wrapper present for DOM stability (e.g. PanelNav expand/collapse)\n // but no tip should appear.\n if (!this.label?.trim()) return;\n this.clearTimers();\n let instant = Date.now() - lastDismissedAt < this.instantReopenMs;\n if (activeTooltip && activeTooltip !== this) {\n activeTooltip.hideInstant();\n instant = true;\n }\n // eslint-disable-next-line @typescript-eslint/no-this-alias -- module singleton tracks the active instance\n activeTooltip = this;\n this.skipEnterAnimation = instant;\n if (instant) {\n this.mountPopup();\n return;\n }\n this.delayTimer = setTimeout(() => {\n this.delayTimer = null;\n if (!this.label?.trim()) return;\n this.mountPopup();\n }, this.hoverDelayMs);\n }\n\n private hide() {\n this.clearTimers();\n if (!this.popupEl && !this.isOpen) return;\n lastDismissedAt = Date.now();\n if (!this.popupEl) {\n this.isOpen = false;\n return;\n }\n this.popupEl.classList.remove('tooltip-popup--instant');\n this.popupEl.classList.add('tooltip-popup--closing');\n if (this.fadeOutMs === 0) {\n if (activeTooltip === this) activeTooltip = null;\n this.destroyPopup();\n this.isOpen = false;\n this.skipEnterAnimation = false;\n return;\n }\n this.closeTimer = setTimeout(() => {\n this.closeTimer = null;\n if (activeTooltip === this) activeTooltip = null;\n this.destroyPopup();\n this.isOpen = false;\n this.skipEnterAnimation = false;\n }, this.fadeOutMs);\n }\n\n private mountPopup() {\n if (!this.popupEl) {\n const tip = document.createElement('div');\n tip.id = this.tooltipId;\n tip.setAttribute('role', 'tooltip');\n // `sc-ds-tooltip` is required for Stencil scoped CSS to match outside the host.\n tip.className = 'tooltip-popup ds-forced-colors-scope sc-ds-tooltip';\n Object.assign(tip.style, {\n position: 'fixed',\n left: '0',\n top: '0',\n zIndex: 'var(--dimension-z-index-tooltip)',\n pointerEvents: 'none',\n });\n document.body.appendChild(tip);\n this.popupEl = tip;\n }\n\n this.setupOpenListeners();\n this.linkDescribedBy();\n this.popupEl.classList.toggle('tooltip-popup--instant', this.skipEnterAnimation);\n this.popupEl.classList.remove('tooltip-popup--closing');\n this.renderPopupContent();\n this.schedulePosition();\n this.isOpen = true;\n }\n\n private renderPopupContent() {\n if (!this.popupEl) return;\n const textVariant = CONTROL_TEXT_VARIANT[this.size];\n const density =\n this.size === 'md' ? 'ds-control--md' : this.size === 'sm' ? 'ds-control--sm' : 'ds-control--xs';\n // Every layout node needs `sc-ds-tooltip` so scoped selectors (`.foo.sc-ds-tooltip`) match.\n const sc = 'sc-ds-tooltip';\n\n const inner = document.createElement('div');\n inner.className = `tooltip-inner ${density} ${sc}`;\n\n const appendShortcut = (position: 'start' | 'end') => {\n if (!this.shortcutKey || this.shortcutKeyPosition !== position) return;\n const group = document.createElement('span');\n group.className = `key-hint-group ${sc}`;\n group.setAttribute('aria-hidden', 'true');\n\n for (const label of shortcutKeyLabels(this.shortcutKey)) {\n const hint = document.createElement('span');\n const wide = Array.from(label).length > 1 ? ' key-hint--wide' : '';\n hint.className = `key-hint${wide} ${sc}`;\n const text = document.createElement('span');\n text.className = `key-hint__label ${sc}`;\n text.textContent = label;\n hint.appendChild(text);\n group.appendChild(hint);\n }\n inner.appendChild(group);\n };\n\n appendShortcut('start');\n\n // ds-text is the layout box (metric-box + 2px control label-inset).\n // Inline styles required — see styleTooltipLabel.\n const label = createDsText({ variant: textVariant, wrap: 'nowrap', text: this.label });\n styleTooltipLabel(label);\n inner.appendChild(label);\n\n appendShortcut('end');\n\n this.popupEl.replaceChildren(inner);\n }\n\n render() {\n return (\n <Host style={{ display: 'contents' }}>\n <slot onSlotchange={this.handleSlotChange} />\n </Host>\n );\n }\n}\n",
303
304
  "type": "registry:ui"
304
305
  }
305
306
  ]
@@ -48,6 +48,7 @@ export { DsMobileHeader } from "./ds-mobile-header.js";
48
48
  export { DsMobileSectionSwitcher } from "./ds-mobile-section-switcher.js";
49
49
  export { DsMobileSheetNav } from "./ds-mobile-sheet-nav.js";
50
50
  export { DsModal } from "./ds-modal.js";
51
+ export { DsPagination } from "./ds-pagination.js";
51
52
  export { DsPanelNav } from "./ds-panel-nav.js";
52
53
  export { DsPanelSubNav } from "./ds-panel-sub-nav.js";
53
54
  export { DsPanelToolHeader } from "./ds-panel-tool-header.js";
@@ -49,6 +49,7 @@ export { DsMobileHeader } from "./ds-mobile-header.js";
49
49
  export { DsMobileSectionSwitcher } from "./ds-mobile-section-switcher.js";
50
50
  export { DsMobileSheetNav } from "./ds-mobile-sheet-nav.js";
51
51
  export { DsModal } from "./ds-modal.js";
52
+ export { DsPagination } from "./ds-pagination.js";
52
53
  export { DsPanelNav } from "./ds-panel-nav.js";
53
54
  export { DsPanelSubNav } from "./ds-panel-sub-nav.js";
54
55
  export { DsPanelToolHeader } from "./ds-panel-tool-header.js";
@@ -0,0 +1,12 @@
1
+ /**
2
+ * This file was automatically generated by the Stencil React Output Target.
3
+ * Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
4
+ */
5
+ import type { EventName, StencilReactComponent } from './react-runtime.js';
6
+ import { type DsPaginationCustomEvent, type PaginationChangeDetail } from "@ds-mo/ui";
7
+ import type { Components } from "@ds-mo/ui/dist/components";
8
+ import { DsPagination as DsPaginationElement } from "@ds-mo/ui/dist/components/ds-pagination.js";
9
+ export type DsPaginationEvents = {
10
+ onDsChange: EventName<DsPaginationCustomEvent<PaginationChangeDetail>>;
11
+ };
12
+ export declare const DsPagination: StencilReactComponent<DsPaginationElement, DsPaginationEvents, Components.DsPagination>;
@@ -0,0 +1,12 @@
1
+ 'use client';
2
+ import { createComponent } from './react-runtime.js';
3
+ import React from 'react';
4
+ import { DsPagination as DsPaginationElement, defineCustomElement as defineDsPagination } from "@ds-mo/ui/dist/components/ds-pagination.js";
5
+ export const DsPagination = /*@__PURE__*/ createComponent({
6
+ tagName: 'ds-pagination',
7
+ elementClass: DsPaginationElement,
8
+ // @ts-ignore - ignore potential React type mismatches between the Stencil Output Target and your project.
9
+ react: React,
10
+ events: { onDsChange: 'dsChange' },
11
+ defineCustomElement: defineDsPagination
12
+ });
@@ -3,7 +3,7 @@
3
3
  * Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
4
4
  */
5
5
  import type { EventName, StencilReactComponent } from './react-runtime.js';
6
- import { type DsTableCustomEvent, type TableCellActionDetail, type TableGroupCollapseChangeDetail, type TableGroupLoadMoreDetail, type TableLoadMoreDetail, type TableRowActivateDetail, type TableSelectionChangeDetail, type TableSortChangeDetail } from "@ds-mo/ui";
6
+ import { type DsTableCustomEvent, type PaginationChangeDetail, type TableCellActionDetail, type TableGroupCollapseChangeDetail, type TableGroupLoadMoreDetail, type TableLoadMoreDetail, type TableRowActivateDetail, type TableSelectionChangeDetail, type TableSortChangeDetail } from "@ds-mo/ui";
7
7
  import type { Components } from "@ds-mo/ui/dist/components";
8
8
  import { DsTable as DsTableElement } from "@ds-mo/ui/dist/components/ds-table.js";
9
9
  export type DsTableEvents = {
@@ -12,6 +12,7 @@ export type DsTableEvents = {
12
12
  onDsSelectionChange: EventName<DsTableCustomEvent<TableSelectionChangeDetail>>;
13
13
  onDsLoadMore: EventName<DsTableCustomEvent<TableLoadMoreDetail>>;
14
14
  onDsGroupLoadMore: EventName<DsTableCustomEvent<TableGroupLoadMoreDetail>>;
15
+ onDsPaginationChange: EventName<DsTableCustomEvent<PaginationChangeDetail>>;
15
16
  onDsCellAction: EventName<DsTableCustomEvent<TableCellActionDetail>>;
16
17
  onDsRowActivate: EventName<DsTableCustomEvent<TableRowActivateDetail>>;
17
18
  };
@@ -13,6 +13,7 @@ export const DsTable = /*@__PURE__*/ createComponent({
13
13
  onDsSelectionChange: 'dsSelectionChange',
14
14
  onDsLoadMore: 'dsLoadMore',
15
15
  onDsGroupLoadMore: 'dsGroupLoadMore',
16
+ onDsPaginationChange: 'dsPaginationChange',
16
17
  onDsCellAction: 'dsCellAction',
17
18
  onDsRowActivate: 'dsRowActivate'
18
19
  },
@@ -1172,6 +1172,12 @@
1172
1172
  pointer-events: none;
1173
1173
  }
1174
1174
 
1175
+ /* A section header yields its divider only when no member or load row follows
1176
+ * it and the section itself is the table's final body. */
1177
+ :where(.ds-table__group:last-child > .ds-table__group-row:last-child .ds-table__group-content)::after {
1178
+ display: none;
1179
+ }
1180
+
1175
1181
  :where(.ds-table__table--selectable .ds-table__group-content),
1176
1182
  :where(.ds-table__sticky-group--selectable .ds-table__group-content) {
1177
1183
  /* Selection lane is the real 40px column — don't add start pad or flex gap. */
@@ -1204,7 +1210,7 @@
1204
1210
  align-items: center;
1205
1211
  min-inline-size: 0;
1206
1212
  flex: 1 1 auto;
1207
- gap: var(--dimension-space-100);
1213
+ gap: var(--dimension-space-050);
1208
1214
  padding-block: 0;
1209
1215
  padding-inline: var(--dimension-space-050);
1210
1216
  }
@@ -1216,10 +1222,9 @@
1216
1222
  );
1217
1223
  }
1218
1224
 
1225
+ :where(.ds-table__group-separator),
1219
1226
  :where(.ds-table__group-count) {
1220
- display: inline-flex;
1221
1227
  flex: 0 0 auto;
1222
- border-radius: var(--dimension-radius-half);
1223
1228
  }
1224
1229
 
1225
1230
  :where(.ds-table__group-toggle) {
@@ -1287,6 +1292,7 @@
1287
1292
 
1288
1293
  :where(.ds-table__skeleton-row:last-child .ds-table__cell),
1289
1294
  :where(.ds-table__load-body .ds-table__load-row:last-child .ds-table__load-cell),
1295
+ :where(.ds-table__group:last-child > .ds-table__group-load-row:last-child .ds-table__load-cell),
1290
1296
  :where(.ds-table__state-row:last-child .ds-table__state-cell),
1291
1297
  :where(.ds-table__body:last-child .ds-table__row:last-child .ds-table__cell) {
1292
1298
  border-block-end: 0;
@@ -0,0 +1,28 @@
1
+ import { EventEmitter } from '../../stencil-public-runtime';
2
+ import type { PaginationChangeDetail } from './pagination-types';
3
+ export declare class Pagination {
4
+ /** Controlled zero-based page index. */
5
+ pageIndex: number;
6
+ /** Controlled number of top-level items on a full page. */
7
+ pageSize: number;
8
+ /** Controlled total number of top-level items across every page. */
9
+ totalItems: number;
10
+ /** Available page sizes. Assign arrays through JavaScript. */
11
+ pageSizeOptions: number[];
12
+ /** Localized plural noun used by assistive range announcements. */
13
+ itemLabel: string;
14
+ /** Visible page-size control label. */
15
+ pageSizeLabel: string;
16
+ /** Accessible name for the pagination navigation region. */
17
+ label: string;
18
+ /** Prevent interaction while the owner replaces the current data page. */
19
+ loading: boolean;
20
+ /** Emits the complete next controlled state after a page or page-size request. */
21
+ dsChange: EventEmitter<PaginationChangeDetail>;
22
+ private readonly pageSizeControlId;
23
+ private requestPage;
24
+ private requestPageSize;
25
+ private get resolvedState();
26
+ render(): any;
27
+ }
28
+ //# sourceMappingURL=Pagination.d.ts.map
@@ -0,0 +1,3 @@
1
+ export { Pagination } from './Pagination';
2
+ export type { PaginationChangeDetail, PaginationChangeReason, PaginationState, } from './pagination-types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,13 @@
1
+ import type { PaginationState } from './pagination-types';
2
+ export declare const DEFAULT_PAGE_SIZE_OPTIONS: readonly [25, 50, 100, 200];
3
+ export interface ResolvedPaginationState {
4
+ pageIndex: number;
5
+ pageSize: number;
6
+ totalItems: number;
7
+ totalPages: number;
8
+ firstItem: number;
9
+ lastItem: number;
10
+ pageSizeOptions: number[];
11
+ }
12
+ export declare function resolvePaginationState(state: Pick<PaginationState, 'pageIndex' | 'pageSize' | 'totalItems' | 'pageSizeOptions'>): ResolvedPaginationState;
13
+ //# sourceMappingURL=pagination-model.d.ts.map
@@ -0,0 +1,25 @@
1
+ export type PaginationChangeReason = 'page' | 'page-size';
2
+ /** Controlled pagination state shared by Pagination and pagination-aware consumers. */
3
+ export interface PaginationState {
4
+ /** Zero-based page index. */
5
+ pageIndex: number;
6
+ /** Number of top-level items on a full page. */
7
+ pageSize: number;
8
+ /** Total number of top-level items across every page. */
9
+ totalItems: number;
10
+ /** Available page sizes. Defaults to 25, 50, 100, and 200. */
11
+ pageSizeOptions?: number[];
12
+ /** Localized plural noun used by assistive range announcements. */
13
+ itemLabel?: string;
14
+ /** Visible label for the page-size selector. */
15
+ pageSizeLabel?: string;
16
+ /** Accessible name for the pagination navigation region. */
17
+ ariaLabel?: string;
18
+ }
19
+ /** Complete next controlled state emitted for a page or page-size request. */
20
+ export interface PaginationChangeDetail extends PaginationState {
21
+ previousPageIndex: number;
22
+ previousPageSize: number;
23
+ reason: PaginationChangeReason;
24
+ }
25
+ //# sourceMappingURL=pagination-types.d.ts.map
@@ -1,5 +1,6 @@
1
1
  import { EventEmitter } from '../../stencil-public-runtime';
2
- import type { TableCaptionVisibility, TableCellActionDetail, TableColumn, TableGroup, TableGroupCollapseChangeDetail, TableGroupLoadMoreDetail, TableGroupingState, TableLoadMoreDetail, TableLoadMoreMode, TableRow, TableRowActivateDetail, TableSelectionChangeDetail, TableSelectionMode, TableSortChangeDetail, TableSortState } from './table-types';
2
+ import type { PaginationChangeDetail } from '../Pagination/pagination-types';
3
+ import type { TableCaptionVisibility, TableCellActionDetail, TableColumn, TableDataMode, TableGroup, TableGroupCollapseChangeDetail, TableGroupLoadMoreDetail, TableGroupingState, TableLoadMoreDetail, TableLoadMoreMode, TablePaginationState, TableRow, TableRowActivateDetail, TableSelectionChangeDetail, TableSelectionMode, TableSortChangeDetail, TableSortState } from './table-types';
3
4
  export declare class Table {
4
5
  el: HTMLElement;
5
6
  /** Stable column definitions. Assign through JavaScript. */
@@ -52,8 +53,10 @@ export declare class Table {
52
53
  errorHeading: string;
53
54
  errorBody: string;
54
55
  emptyCellLabel: string;
55
- /** Enable application-owned incremental loading without pagination. */
56
- lazyLoading: boolean;
56
+ /** Top-level data-window strategy. Group member loading remains group-owned. */
57
+ dataMode: TableDataMode;
58
+ /** Controlled top-level pagination state. Required when dataMode is pagination. */
59
+ pagination: TablePaginationState | null;
57
60
  loadMoreMode: TableLoadMoreMode;
58
61
  hasMore: boolean;
59
62
  loadingMore: boolean;
@@ -84,6 +87,7 @@ export declare class Table {
84
87
  dsSelectionChange: EventEmitter<TableSelectionChangeDetail>;
85
88
  dsLoadMore: EventEmitter<TableLoadMoreDetail>;
86
89
  dsGroupLoadMore: EventEmitter<TableGroupLoadMoreDetail>;
90
+ dsPaginationChange: EventEmitter<PaginationChangeDetail>;
87
91
  dsCellAction: EventEmitter<TableCellActionDetail>;
88
92
  dsRowActivate: EventEmitter<TableRowActivateDetail>;
89
93
  private overflowStart;
@@ -101,6 +105,7 @@ export declare class Table {
101
105
  private stickyHeaderTableEl;
102
106
  private tableEl;
103
107
  private sentinelEl;
108
+ private incrementalWindowActive;
104
109
  private readonly groupSentinelEls;
105
110
  private previousModelWarning;
106
111
  private hasLoaded;
@@ -126,10 +131,12 @@ export declare class Table {
126
131
  handleDataChange(): void;
127
132
  handleLoadIdentityChange(): void;
128
133
  handleLazyConfigurationChange(): void;
134
+ handleDataModeChange(): void;
129
135
  handleLoadingMoreChange(loading: boolean): void;
130
136
  handleLoadMoreErrorChange(error: string | undefined): void;
131
137
  handleHasMoreChange(hasMore: boolean, hadMore: boolean): void;
132
138
  private get grouped();
139
+ private get hasIncrementalState();
133
140
  private get selectable();
134
141
  private get documentStickyHeader();
135
142
  private get fixedHeight();
@@ -1,2 +1,2 @@
1
- export type { TableCaptionVisibility, TableCellAlign, TableCellAction, TableCellActionDetail, TableCellBlank, TableCellEmpty, TableCellIcon, TableCellImage, TableCellPrimaryText, TableCellText, TableCellTag, TableCellTagVariant, TableCellValue, TableColumn, TableColumnSticky, TableColumnWidth, TableGroup, TableGroupCollapseChangeDetail, TableGroupLoadMoreDetail, TableGroupIntent, TableHeaderSegment, TableGroupingState, TableLoadMoreDetail, TableLoadMoreMode, TableLoadMoreReason, TableRow, TableRowActivateDetail, TableSelectionChangeDetail, TableSelectionMode, TableSortChangeDetail, TableSortDirection, TableSortState, } from './table-types';
1
+ export type { TableCaptionVisibility, TableCellAlign, TableCellAction, TableCellActionDetail, TableCellBlank, TableCellEmpty, TableCellIcon, TableCellImage, TableCellPrimaryText, TableCellText, TableCellTag, TableCellTagVariant, TableCellValue, TableColumn, TableColumnSticky, TableColumnWidth, TableDataMode, TableGroup, TableGroupCollapseChangeDetail, TableGroupLoadMoreDetail, TableGroupIntent, TableHeaderSegment, TableGroupingState, TableLoadMoreDetail, TableLoadMoreMode, TableLoadMoreReason, TablePaginationState, TableRow, TableRowActivateDetail, TableSelectionChangeDetail, TableSelectionMode, TableSortChangeDetail, TableSortDirection, TableSortState, } from './table-types';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -3,8 +3,8 @@ import type { TableColumn, TableGroup, TableGroupIntent, TableRow, TableSelectio
3
3
  export interface TableGroupRenderModel {
4
4
  group: TableGroup;
5
5
  count: number;
6
+ loadedCount: number;
6
7
  countLabel: string;
7
- countIntent: TableGroupIntent;
8
8
  intent: TableGroupIntent | undefined;
9
9
  intentClass: string | undefined;
10
10
  labelColor: ReturnType<typeof tableGroupLabelColor>;
@@ -1,12 +1,14 @@
1
1
  import type { TagContrast, TagIntent } from '../Tag/Tag';
2
2
  import type { IconColor } from '../Icon/Icon';
3
3
  import type { TextColor } from '../Text/text-types';
4
+ import type { PaginationState } from '../Pagination/pagination-types';
4
5
  export type TableSortDirection = 'asc' | 'desc';
5
6
  export type TableSelectionMode = 'none' | 'multiple';
6
7
  export type TableCellAlign = 'start' | 'center' | 'end';
7
8
  export type TableColumnSticky = 'start' | 'end';
8
9
  export type TableCaptionVisibility = 'visible' | 'hidden';
9
10
  export type TableLoadMoreMode = 'auto' | 'manual';
11
+ export type TableDataMode = 'infinite' | 'pagination';
10
12
  export type TableLoadMoreReason = 'auto' | 'manual' | 'retry';
11
13
  export type TableColumnWidth = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
12
14
  /** Semantic color for a group section header (faint surface + bold title). */
@@ -166,7 +168,7 @@ export interface TableGroup {
166
168
  value?: string | number;
167
169
  /** Total server-side member count; may exceed the currently loaded row count. */
168
170
  totalCount?: number;
169
- /** Optional localized accessible count phrase such as “18 vehicles”; the visible tag remains numeric. */
171
+ /** Optional localized total-count phrase such as “166 events” for accessible loaded progress. */
170
172
  countLabel?: string;
171
173
  /**
172
174
  * Optional semantic intent for the group section header. Applies a faint
@@ -217,5 +219,7 @@ export interface TableGroupCollapseChangeDetail {
217
219
  export interface TableRowActivateDetail {
218
220
  rowId: string;
219
221
  }
222
+ /** Controlled top-level pagination state. Rows or groups contain only the active page. */
223
+ export type TablePaginationState = PaginationState;
220
224
  export {};
221
225
  //# sourceMappingURL=table-types.d.ts.map
@@ -38,6 +38,7 @@ export declare class Tooltip {
38
38
  private anchor;
39
39
  private popupEl;
40
40
  private anchorObserver;
41
+ private didLoad;
41
42
  private readonly position;
42
43
  private skipEnterAnimation;
43
44
  private isOpen;
@@ -50,7 +51,9 @@ export declare class Tooltip {
50
51
  private focusHandler;
51
52
  private blurHandler;
52
53
  private escapeHandler;
54
+ connectedCallback(): void;
53
55
  componentDidLoad(): void;
56
+ private connectAnchor;
54
57
  disconnectedCallback(): void;
55
58
  onContentOrPositionChange(): void;
56
59
  private get viewportPadPx();