@k2b/ui 0.9.1 → 0.10.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 (174) hide show
  1. package/dist/browser/actions/Button.js +15 -7
  2. package/dist/browser/actions/Button.js.map +1 -1
  3. package/dist/browser/actions/ContextMenu.js +55 -15
  4. package/dist/browser/actions/ContextMenu.js.map +1 -1
  5. package/dist/browser/actions/Dropdown.js +39 -15
  6. package/dist/browser/actions/Dropdown.js.map +1 -1
  7. package/dist/browser/actions/FilterChip.js +1 -7
  8. package/dist/browser/actions/FilterChip.js.map +1 -1
  9. package/dist/browser/actions/SplitButton.js +0 -3
  10. package/dist/browser/actions/SplitButton.js.map +1 -1
  11. package/dist/browser/chat/ChatComposer.js +78 -91
  12. package/dist/browser/chat/ChatComposer.js.map +1 -1
  13. package/dist/browser/chat/ChatContextPopup.js +47 -21
  14. package/dist/browser/chat/ChatContextPopup.js.map +1 -1
  15. package/dist/browser/chat/ChatPrimitives.js +221 -232
  16. package/dist/browser/chat/ChatPrimitives.js.map +1 -1
  17. package/dist/browser/chat/index.js +3 -2
  18. package/dist/browser/chat/index.js.map +1 -1
  19. package/dist/browser/content/Chart.js +99 -106
  20. package/dist/browser/content/Chart.js.map +1 -1
  21. package/dist/browser/content/ChartExplorer.js +3 -6
  22. package/dist/browser/content/ChartExplorer.js.map +1 -1
  23. package/dist/browser/content/FileBrowser.js +1 -1
  24. package/dist/browser/content/FileBrowser.js.map +1 -1
  25. package/dist/browser/content/FileTree.js +1 -1
  26. package/dist/browser/content/FileTree.js.map +1 -1
  27. package/dist/browser/content/FileView.js +304 -287
  28. package/dist/browser/content/FileView.js.map +1 -1
  29. package/dist/browser/content/Lightbox.js +18 -0
  30. package/dist/browser/content/Lightbox.js.map +1 -1
  31. package/dist/browser/content/PdfPreview.js +68 -43
  32. package/dist/browser/content/PdfPreview.js.map +1 -1
  33. package/dist/browser/content/ZoomPanViewport.js +51 -52
  34. package/dist/browser/content/ZoomPanViewport.js.map +1 -1
  35. package/dist/browser/feedback/HoverPreview.js +391 -0
  36. package/dist/browser/feedback/HoverPreview.js.map +1 -0
  37. package/dist/browser/feedback/Tooltip.js +50 -8
  38. package/dist/browser/feedback/Tooltip.js.map +1 -1
  39. package/dist/browser/feedback/dialog-core.js +21 -3
  40. package/dist/browser/feedback/dialog-core.js.map +1 -1
  41. package/dist/browser/feedback/index.js +1 -0
  42. package/dist/browser/feedback/index.js.map +1 -1
  43. package/dist/browser/inputs/AutocompleteEditor.js +5 -4
  44. package/dist/browser/inputs/AutocompleteEditor.js.map +1 -1
  45. package/dist/browser/inputs/DatePicker.js +10 -0
  46. package/dist/browser/inputs/DatePicker.js.map +1 -1
  47. package/dist/browser/inputs/FileInputs.js +46 -36
  48. package/dist/browser/inputs/FileInputs.js.map +1 -1
  49. package/dist/browser/inputs/SelectChip.js +2 -1
  50. package/dist/browser/inputs/SelectChip.js.map +1 -1
  51. package/dist/browser/inputs/choice.js +5 -1
  52. package/dist/browser/inputs/choice.js.map +1 -1
  53. package/dist/browser/inputs/markdown/MarkdownEditor.js +173 -173
  54. package/dist/browser/inputs/markdown/MarkdownEditor.js.map +1 -1
  55. package/dist/browser/inputs/markdown/Toolbar.js +31 -34
  56. package/dist/browser/inputs/markdown/Toolbar.js.map +1 -1
  57. package/dist/browser/internal/focus-return.js +61 -0
  58. package/dist/browser/internal/focus-return.js.map +1 -0
  59. package/dist/browser/layout/AppOverview.js +1 -1
  60. package/dist/browser/layout/AppOverview.js.map +1 -1
  61. package/dist/browser/layout/DetailPanel.js +16 -15
  62. package/dist/browser/layout/DetailPanel.js.map +1 -1
  63. package/dist/browser/layout/FloatingWindow.js +3 -1
  64. package/dist/browser/layout/FloatingWindow.js.map +1 -1
  65. package/dist/browser/layout/PanelDialog.js +10 -9
  66. package/dist/browser/layout/PanelDialog.js.map +1 -1
  67. package/dist/browser/layout/Panes.js +82 -81
  68. package/dist/browser/layout/Panes.js.map +1 -1
  69. package/dist/browser/layout/SidebarItemPreview.js +48 -136
  70. package/dist/browser/layout/SidebarItemPreview.js.map +1 -1
  71. package/dist/browser/layout/app-workspace-controller.js +5 -2
  72. package/dist/browser/layout/app-workspace-controller.js.map +1 -1
  73. package/dist/browser/surfaces/ProgressBar.js.map +1 -1
  74. package/dist/browser/surfaces/ProgressRing.js +72 -0
  75. package/dist/browser/surfaces/ProgressRing.js.map +1 -0
  76. package/dist/browser/surfaces/index.js +1 -0
  77. package/dist/browser/surfaces/index.js.map +1 -1
  78. package/dist/ssr/actions/Button.js +15 -7
  79. package/dist/ssr/actions/Button.js.map +1 -1
  80. package/dist/ssr/actions/ContextMenu.js +55 -15
  81. package/dist/ssr/actions/ContextMenu.js.map +1 -1
  82. package/dist/ssr/actions/Dropdown.js +31 -12
  83. package/dist/ssr/actions/Dropdown.js.map +1 -1
  84. package/dist/ssr/actions/FilterChip.js +1 -7
  85. package/dist/ssr/actions/FilterChip.js.map +1 -1
  86. package/dist/ssr/actions/SplitButton.js +0 -3
  87. package/dist/ssr/actions/SplitButton.js.map +1 -1
  88. package/dist/ssr/chat/ChatComposer.js +61 -10
  89. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  90. package/dist/ssr/chat/ChatContextPopup.js +19 -5
  91. package/dist/ssr/chat/ChatContextPopup.js.map +1 -1
  92. package/dist/ssr/chat/ChatPrimitives.js +104 -74
  93. package/dist/ssr/chat/ChatPrimitives.js.map +1 -1
  94. package/dist/ssr/chat/index.js +3 -2
  95. package/dist/ssr/chat/index.js.map +1 -1
  96. package/dist/ssr/content/Chart.js +22 -11
  97. package/dist/ssr/content/Chart.js.map +1 -1
  98. package/dist/ssr/content/ChartExplorer.js +3 -6
  99. package/dist/ssr/content/ChartExplorer.js.map +1 -1
  100. package/dist/ssr/content/FileBrowser.js +1 -1
  101. package/dist/ssr/content/FileBrowser.js.map +1 -1
  102. package/dist/ssr/content/FileTree.js +1 -1
  103. package/dist/ssr/content/FileTree.js.map +1 -1
  104. package/dist/ssr/content/FileView.js +105 -33
  105. package/dist/ssr/content/FileView.js.map +1 -1
  106. package/dist/ssr/content/Lightbox.js +17 -0
  107. package/dist/ssr/content/Lightbox.js.map +1 -1
  108. package/dist/ssr/content/PdfPreview.js +44 -14
  109. package/dist/ssr/content/PdfPreview.js.map +1 -1
  110. package/dist/ssr/content/ZoomPanViewport.js +11 -3
  111. package/dist/ssr/content/ZoomPanViewport.js.map +1 -1
  112. package/dist/ssr/feedback/HoverPreview.js +360 -0
  113. package/dist/ssr/feedback/HoverPreview.js.map +1 -0
  114. package/dist/ssr/feedback/Tooltip.js +50 -8
  115. package/dist/ssr/feedback/Tooltip.js.map +1 -1
  116. package/dist/ssr/feedback/dialog-core.js +21 -3
  117. package/dist/ssr/feedback/dialog-core.js.map +1 -1
  118. package/dist/ssr/feedback/index.js +1 -0
  119. package/dist/ssr/feedback/index.js.map +1 -1
  120. package/dist/ssr/inputs/AutocompleteEditor.js +5 -4
  121. package/dist/ssr/inputs/AutocompleteEditor.js.map +1 -1
  122. package/dist/ssr/inputs/DatePicker.js +5 -0
  123. package/dist/ssr/inputs/DatePicker.js.map +1 -1
  124. package/dist/ssr/inputs/FileInputs.js +29 -11
  125. package/dist/ssr/inputs/FileInputs.js.map +1 -1
  126. package/dist/ssr/inputs/SelectChip.js +2 -1
  127. package/dist/ssr/inputs/SelectChip.js.map +1 -1
  128. package/dist/ssr/inputs/choice.js +5 -1
  129. package/dist/ssr/inputs/choice.js.map +1 -1
  130. package/dist/ssr/inputs/markdown/MarkdownEditor.js +46 -10
  131. package/dist/ssr/inputs/markdown/MarkdownEditor.js.map +1 -1
  132. package/dist/ssr/inputs/markdown/Toolbar.js +30 -2
  133. package/dist/ssr/inputs/markdown/Toolbar.js.map +1 -1
  134. package/dist/ssr/internal/focus-return.js +61 -0
  135. package/dist/ssr/internal/focus-return.js.map +1 -0
  136. package/dist/ssr/layout/AppOverview.js +1 -1
  137. package/dist/ssr/layout/AppOverview.js.map +1 -1
  138. package/dist/ssr/layout/DetailPanel.js +16 -15
  139. package/dist/ssr/layout/DetailPanel.js.map +1 -1
  140. package/dist/ssr/layout/FloatingWindow.js +3 -1
  141. package/dist/ssr/layout/FloatingWindow.js.map +1 -1
  142. package/dist/ssr/layout/PanelDialog.js +10 -9
  143. package/dist/ssr/layout/PanelDialog.js.map +1 -1
  144. package/dist/ssr/layout/Panes.js +39 -8
  145. package/dist/ssr/layout/Panes.js.map +1 -1
  146. package/dist/ssr/layout/SidebarItemPreview.js +36 -108
  147. package/dist/ssr/layout/SidebarItemPreview.js.map +1 -1
  148. package/dist/ssr/layout/app-workspace-controller.js +5 -2
  149. package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
  150. package/dist/ssr/surfaces/ProgressBar.js.map +1 -1
  151. package/dist/ssr/surfaces/ProgressRing.js +31 -0
  152. package/dist/ssr/surfaces/ProgressRing.js.map +1 -0
  153. package/dist/ssr/surfaces/index.js +1 -0
  154. package/dist/ssr/surfaces/index.js.map +1 -1
  155. package/dist/styles.css +1 -1
  156. package/dist/types/actions/Button.d.ts +5 -0
  157. package/dist/types/actions/Dropdown.d.ts +5 -1
  158. package/dist/types/actions/SplitButton.d.ts +1 -0
  159. package/dist/types/chat/ChatContextPopup.d.ts +8 -0
  160. package/dist/types/chat/index.d.ts +3 -2
  161. package/dist/types/feedback/HoverPreview.d.ts +83 -0
  162. package/dist/types/feedback/Tooltip.d.ts +6 -0
  163. package/dist/types/feedback/index.d.ts +2 -0
  164. package/dist/types/feedback/prompts.d.ts +9 -6
  165. package/dist/types/inputs/SelectChip.d.ts +1 -0
  166. package/dist/types/inputs/completion/overlay.d.ts +3 -1
  167. package/dist/types/internal/focus-return.d.ts +20 -0
  168. package/dist/types/intl/Format.d.ts +16 -8
  169. package/dist/types/layout/BottomSheet.d.ts +6 -1
  170. package/dist/types/layout/SidebarItemPreview.d.ts +1 -1
  171. package/dist/types/surfaces/ProgressBar.d.ts +1 -1
  172. package/dist/types/surfaces/ProgressRing.d.ts +13 -0
  173. package/dist/types/surfaces/index.d.ts +2 -0
  174. package/package.json +3 -2
@@ -14,7 +14,8 @@ var _tmpl$ = /*#__PURE__*/_$template(`<i class="ti ti-loader-2 k2b-spin"aria-hid
14
14
  _tmpl$4 = /*#__PURE__*/_$template(`<a>`);
15
15
  import { Link } from "@k2b/ssr/nav";
16
16
  import { Show, splitProps } from "solid-js";
17
- import { Tooltip } from "../feedback/Tooltip";
17
+ import { isServer } from "solid-js/web";
18
+ import { Tooltip, useLabelTooltip } from "../feedback/Tooltip";
18
19
  const buttonClass = className => ["k2b-button", className].filter(Boolean).join(" ");
19
20
  export function Button(props) {
20
21
  const [local, rest] = splitProps(props, ["children", "class", "disabled", "loading", "loadingLabel", "ref", "size", "tooltip", "tooltipDelay", "tooltipPlacement", "type", "variant", "wrap", "align"]);
@@ -195,8 +196,14 @@ export function ButtonLink(props) {
195
196
  return _el$7;
196
197
  })(), _$memo(tooltip)];
197
198
  }
199
+ /**
200
+ * The native title is only the server-rendered, no-JavaScript hint; the
201
+ * hydrated tooltip removes it. Without a tooltip, an explicit title stays.
202
+ */
203
+ export const iconTitle = (title, tooltip) => tooltip === false || tooltip === undefined ? title : isServer && typeof tooltip === "string" ? tooltip : undefined;
198
204
  export function IconButton(props) {
199
- const [local, rest] = splitProps(props, ["children", "class", "label", "loading", "loadingLabel", "tooltip", "variant"]);
205
+ const [local, rest] = splitProps(props, ["children", "class", "label", "loading", "loadingLabel", "title", "tooltip", "variant"]);
206
+ const tooltip = useLabelTooltip(() => local.tooltip, () => local.title ?? local.label);
200
207
  return _$createComponent(Button, _$mergeProps(rest, {
201
208
  get loading() {
202
209
  return local.loading;
@@ -211,10 +218,10 @@ export function IconButton(props) {
211
218
  return _$memo(() => !!local.loading)() ? local.loadingLabel ?? local.label : local.label;
212
219
  },
213
220
  get title() {
214
- return rest.title ?? (local.tooltip === undefined ? local.label : undefined);
221
+ return iconTitle(local.title, tooltip());
215
222
  },
216
223
  get tooltip() {
217
- return local.tooltip;
224
+ return tooltip();
218
225
  },
219
226
  get children() {
220
227
  return _$createComponent(Show, {
@@ -229,7 +236,8 @@ export function IconButton(props) {
229
236
  }));
230
237
  }
231
238
  export function IconButtonLink(props) {
232
- const [local, rest] = splitProps(props, ["children", "class", "label", "tooltip", "variant"]);
239
+ const [local, rest] = splitProps(props, ["children", "class", "label", "title", "tooltip", "variant"]);
240
+ const tooltip = useLabelTooltip(() => local.tooltip, () => local.title ?? local.label);
233
241
  return _$createComponent(ButtonLink, _$mergeProps(rest, {
234
242
  get variant() {
235
243
  return local.variant ?? "ghost";
@@ -241,10 +249,10 @@ export function IconButtonLink(props) {
241
249
  return local.label;
242
250
  },
243
251
  get title() {
244
- return rest.title ?? (local.tooltip === undefined ? local.label : undefined);
252
+ return iconTitle(local.title, tooltip());
245
253
  },
246
254
  get tooltip() {
247
- return local.tooltip;
255
+ return tooltip();
248
256
  },
249
257
  get children() {
250
258
  return local.children;
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["actions/Button.tsx"],"sourcesContent":["import { Link, type LinkNavigateEvent, type NavigationScrollMode } from \"@k2b/ssr/nav\";\nimport { type JSX, Show, splitProps } from \"solid-js\";\nimport { Tooltip, type TooltipPlacement } from \"../feedback/Tooltip\";\n\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"ghost\" | \"text\" | \"subtle\" | \"input\" | \"warning\" | \"danger\" | \"success\" | \"ai\";\nexport type ButtonSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\nexport type ButtonAlign = \"center\" | \"start\";\n\nexport type ButtonProps = JSX.ButtonHTMLAttributes<HTMLButtonElement> & {\n /** Allow a deliberately long or rich label to wrap. Defaults to false. */\n wrap?: boolean;\n /** Start-align the content and let the label fill the button, for full-width rows. Defaults to `\"center\"`. */\n align?: ButtonAlign;\n loading?: boolean;\n loadingLabel?: string;\n size?: ButtonSize;\n variant?: ButtonVariant;\n tooltip?: JSX.Element | false;\n tooltipDelay?: number;\n tooltipPlacement?: TooltipPlacement;\n};\n\nexport type ButtonLinkProps = Omit<JSX.AnchorHTMLAttributes<HTMLAnchorElement>, \"onClick\"> & {\n /** Allow a deliberately long or rich label to wrap. Defaults to false. */\n wrap?: boolean;\n /** Start-align the content and let the label fill the button, for full-width rows. Defaults to `\"center\"`. */\n align?: ButtonAlign;\n navigation?: \"document\" | \"enhanced\";\n onClick?: JSX.EventHandlerUnion<HTMLAnchorElement, MouseEvent>;\n onNavigate?: (event: LinkNavigateEvent) => void | Promise<void>;\n replace?: boolean;\n scroll?: NavigationScrollMode;\n size?: ButtonSize;\n variant?: ButtonVariant;\n tooltip?: JSX.Element | false;\n tooltipDelay?: number;\n tooltipPlacement?: TooltipPlacement;\n};\n\nconst buttonClass = (className?: string): string => [\"k2b-button\", className].filter(Boolean).join(\" \");\n\nexport function Button(props: ButtonProps): JSX.Element {\n const [local, rest] = splitProps(props, [\n \"children\",\n \"class\",\n \"disabled\",\n \"loading\",\n \"loadingLabel\",\n \"ref\",\n \"size\",\n \"tooltip\",\n \"tooltipDelay\",\n \"tooltipPlacement\",\n \"type\",\n \"variant\",\n \"wrap\",\n \"align\",\n ]);\n let target: HTMLButtonElement | undefined;\n const setRef = (element: HTMLButtonElement) => {\n target = element;\n if (typeof local.ref === \"function\") local.ref(element);\n };\n\n const control = (\n <button\n {...rest}\n ref={setRef}\n type={local.type ?? \"button\"}\n class={buttonClass(local.class)}\n data-size={local.size ?? \"md\"}\n data-variant={local.variant ?? \"primary\"}\n data-wrap={local.wrap || undefined}\n data-align={local.align === \"start\" ? \"start\" : undefined}\n disabled={local.disabled || local.loading}\n aria-busy={local.loading ? \"true\" : undefined}\n >\n <Show when={local.loading}>\n <i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\" />\n </Show>\n <span class=\"k2b-button__label\">\n <Show when={local.loading && local.loadingLabel} fallback={local.children}>\n {local.loadingLabel}\n </Show>\n </span>\n </button>\n );\n\n return (\n <>\n {control}\n <Show when={local.tooltip !== false && local.tooltip !== undefined}>\n <Tooltip\n content={local.tooltip as JSX.Element}\n target={() => target}\n delay={local.tooltipDelay}\n disabled={local.disabled || local.loading}\n placement={local.tooltipPlacement}\n />\n </Show>\n </>\n );\n}\n\nexport function ButtonLink(props: ButtonLinkProps): JSX.Element {\n const [local, rest] = splitProps(props, [\n \"children\",\n \"class\",\n \"href\",\n \"navigation\",\n \"onClick\",\n \"onNavigate\",\n \"replace\",\n \"ref\",\n \"scroll\",\n \"size\",\n \"tooltip\",\n \"tooltipDelay\",\n \"tooltipPlacement\",\n \"variant\",\n \"wrap\",\n \"align\",\n ]);\n let target: HTMLAnchorElement | undefined;\n const setRef = (element: HTMLAnchorElement) => {\n target = element;\n if (typeof local.ref === \"function\") local.ref(element);\n };\n const className = buttonClass(local.class);\n const content = <span class=\"k2b-button__label\">{local.children}</span>;\n\n const tooltip = () => (\n <Show when={local.tooltip !== false && local.tooltip !== undefined}>\n <Tooltip content={local.tooltip as JSX.Element} target={() => target} delay={local.tooltipDelay} placement={local.tooltipPlacement} />\n </Show>\n );\n\n if (local.navigation === \"enhanced\" && local.href && local.onNavigate) {\n return (\n <>\n <Link\n {...rest}\n ref={setRef}\n href={local.href}\n class={className}\n data-size={local.size ?? \"md\"}\n data-variant={local.variant ?? \"primary\"}\n data-wrap={local.wrap || undefined}\n data-align={local.align === \"start\" ? \"start\" : undefined}\n onClick={local.onClick}\n onNavigate={local.onNavigate}\n replace={local.replace}\n scroll={local.scroll}\n >\n {content}\n </Link>\n {tooltip()}\n </>\n );\n }\n\n return (\n <>\n <a\n {...rest}\n ref={setRef}\n href={local.href}\n class={className}\n data-size={local.size ?? \"md\"}\n data-variant={local.variant ?? \"primary\"}\n data-wrap={local.wrap || undefined}\n data-align={local.align === \"start\" ? \"start\" : undefined}\n onClick={local.onClick}\n >\n {content}\n </a>\n {tooltip()}\n </>\n );\n}\n\nexport type IconButtonProps = Omit<ButtonProps, \"children\"> & {\n children: JSX.Element;\n label: string;\n};\n\nexport type IconButtonLinkProps = Omit<ButtonLinkProps, \"children\"> & {\n children: JSX.Element;\n label: string;\n};\n\nexport function IconButton(props: IconButtonProps): JSX.Element {\n const [local, rest] = splitProps(props, [\"children\", \"class\", \"label\", \"loading\", \"loadingLabel\", \"tooltip\", \"variant\"]);\n\n return (\n <Button\n {...rest}\n loading={local.loading}\n variant={local.variant ?? \"ghost\"}\n class={`k2b-icon-button ${local.class ?? \"\"}`}\n aria-label={local.loading ? (local.loadingLabel ?? local.label) : local.label}\n title={rest.title ?? (local.tooltip === undefined ? local.label : undefined)}\n tooltip={local.tooltip}\n >\n <Show when={!local.loading}>{local.children}</Show>\n </Button>\n );\n}\n\nexport function IconButtonLink(props: IconButtonLinkProps): JSX.Element {\n const [local, rest] = splitProps(props, [\"children\", \"class\", \"label\", \"tooltip\", \"variant\"]);\n\n return (\n <ButtonLink\n {...rest}\n variant={local.variant ?? \"ghost\"}\n class={`k2b-icon-button ${local.class ?? \"\"}`}\n aria-label={local.label}\n title={rest.title ?? (local.tooltip === undefined ? local.label : undefined)}\n tooltip={local.tooltip}\n >\n {local.children}\n </ButtonLink>\n );\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["actions/Button.tsx"],"sourcesContent":["import { Link, type LinkNavigateEvent, type NavigationScrollMode } from \"@k2b/ssr/nav\";\nimport { type JSX, Show, splitProps } from \"solid-js\";\nimport { isServer } from \"solid-js/web\";\nimport { Tooltip, type TooltipPlacement, useLabelTooltip } from \"../feedback/Tooltip\";\n\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"ghost\" | \"text\" | \"subtle\" | \"input\" | \"warning\" | \"danger\" | \"success\" | \"ai\";\nexport type ButtonSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\nexport type ButtonAlign = \"center\" | \"start\";\n\nexport type ButtonProps = JSX.ButtonHTMLAttributes<HTMLButtonElement> & {\n /** Allow a deliberately long or rich label to wrap. Defaults to false. */\n wrap?: boolean;\n /** Start-align the content and let the label fill the button, for full-width rows. Defaults to `\"center\"`. */\n align?: ButtonAlign;\n loading?: boolean;\n loadingLabel?: string;\n size?: ButtonSize;\n variant?: ButtonVariant;\n tooltip?: JSX.Element | false;\n tooltipDelay?: number;\n tooltipPlacement?: TooltipPlacement;\n};\n\nexport type ButtonLinkProps = Omit<JSX.AnchorHTMLAttributes<HTMLAnchorElement>, \"onClick\"> & {\n /** Allow a deliberately long or rich label to wrap. Defaults to false. */\n wrap?: boolean;\n /** Start-align the content and let the label fill the button, for full-width rows. Defaults to `\"center\"`. */\n align?: ButtonAlign;\n navigation?: \"document\" | \"enhanced\";\n onClick?: JSX.EventHandlerUnion<HTMLAnchorElement, MouseEvent>;\n onNavigate?: (event: LinkNavigateEvent) => void | Promise<void>;\n replace?: boolean;\n scroll?: NavigationScrollMode;\n size?: ButtonSize;\n variant?: ButtonVariant;\n tooltip?: JSX.Element | false;\n tooltipDelay?: number;\n tooltipPlacement?: TooltipPlacement;\n};\n\nconst buttonClass = (className?: string): string => [\"k2b-button\", className].filter(Boolean).join(\" \");\n\nexport function Button(props: ButtonProps): JSX.Element {\n const [local, rest] = splitProps(props, [\n \"children\",\n \"class\",\n \"disabled\",\n \"loading\",\n \"loadingLabel\",\n \"ref\",\n \"size\",\n \"tooltip\",\n \"tooltipDelay\",\n \"tooltipPlacement\",\n \"type\",\n \"variant\",\n \"wrap\",\n \"align\",\n ]);\n let target: HTMLButtonElement | undefined;\n const setRef = (element: HTMLButtonElement) => {\n target = element;\n if (typeof local.ref === \"function\") local.ref(element);\n };\n\n const control = (\n <button\n {...rest}\n ref={setRef}\n type={local.type ?? \"button\"}\n class={buttonClass(local.class)}\n data-size={local.size ?? \"md\"}\n data-variant={local.variant ?? \"primary\"}\n data-wrap={local.wrap || undefined}\n data-align={local.align === \"start\" ? \"start\" : undefined}\n disabled={local.disabled || local.loading}\n aria-busy={local.loading ? \"true\" : undefined}\n >\n <Show when={local.loading}>\n <i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\" />\n </Show>\n <span class=\"k2b-button__label\">\n <Show when={local.loading && local.loadingLabel} fallback={local.children}>\n {local.loadingLabel}\n </Show>\n </span>\n </button>\n );\n\n return (\n <>\n {control}\n <Show when={local.tooltip !== false && local.tooltip !== undefined}>\n <Tooltip\n content={local.tooltip as JSX.Element}\n target={() => target}\n delay={local.tooltipDelay}\n disabled={local.disabled || local.loading}\n placement={local.tooltipPlacement}\n />\n </Show>\n </>\n );\n}\n\nexport function ButtonLink(props: ButtonLinkProps): JSX.Element {\n const [local, rest] = splitProps(props, [\n \"children\",\n \"class\",\n \"href\",\n \"navigation\",\n \"onClick\",\n \"onNavigate\",\n \"replace\",\n \"ref\",\n \"scroll\",\n \"size\",\n \"tooltip\",\n \"tooltipDelay\",\n \"tooltipPlacement\",\n \"variant\",\n \"wrap\",\n \"align\",\n ]);\n let target: HTMLAnchorElement | undefined;\n const setRef = (element: HTMLAnchorElement) => {\n target = element;\n if (typeof local.ref === \"function\") local.ref(element);\n };\n const className = buttonClass(local.class);\n const content = <span class=\"k2b-button__label\">{local.children}</span>;\n\n const tooltip = () => (\n <Show when={local.tooltip !== false && local.tooltip !== undefined}>\n <Tooltip content={local.tooltip as JSX.Element} target={() => target} delay={local.tooltipDelay} placement={local.tooltipPlacement} />\n </Show>\n );\n\n if (local.navigation === \"enhanced\" && local.href && local.onNavigate) {\n return (\n <>\n <Link\n {...rest}\n ref={setRef}\n href={local.href}\n class={className}\n data-size={local.size ?? \"md\"}\n data-variant={local.variant ?? \"primary\"}\n data-wrap={local.wrap || undefined}\n data-align={local.align === \"start\" ? \"start\" : undefined}\n onClick={local.onClick}\n onNavigate={local.onNavigate}\n replace={local.replace}\n scroll={local.scroll}\n >\n {content}\n </Link>\n {tooltip()}\n </>\n );\n }\n\n return (\n <>\n <a\n {...rest}\n ref={setRef}\n href={local.href}\n class={className}\n data-size={local.size ?? \"md\"}\n data-variant={local.variant ?? \"primary\"}\n data-wrap={local.wrap || undefined}\n data-align={local.align === \"start\" ? \"start\" : undefined}\n onClick={local.onClick}\n >\n {content}\n </a>\n {tooltip()}\n </>\n );\n}\n\nexport type IconButtonProps = Omit<ButtonProps, \"children\"> & {\n children: JSX.Element;\n label: string;\n};\n\nexport type IconButtonLinkProps = Omit<ButtonLinkProps, \"children\"> & {\n children: JSX.Element;\n label: string;\n};\n\n/**\n * The native title is only the server-rendered, no-JavaScript hint; the\n * hydrated tooltip removes it. Without a tooltip, an explicit title stays.\n */\nexport const iconTitle = (title: string | undefined, tooltip: JSX.Element | false | undefined): string | undefined =>\n tooltip === false || tooltip === undefined ? title : isServer && typeof tooltip === \"string\" ? tooltip : undefined;\n\nexport function IconButton(props: IconButtonProps): JSX.Element {\n const [local, rest] = splitProps(props, [\"children\", \"class\", \"label\", \"loading\", \"loadingLabel\", \"title\", \"tooltip\", \"variant\"]);\n const tooltip = useLabelTooltip(\n () => local.tooltip,\n () => local.title ?? local.label,\n );\n\n return (\n <Button\n {...rest}\n loading={local.loading}\n variant={local.variant ?? \"ghost\"}\n class={`k2b-icon-button ${local.class ?? \"\"}`}\n aria-label={local.loading ? (local.loadingLabel ?? local.label) : local.label}\n title={iconTitle(local.title, tooltip())}\n tooltip={tooltip()}\n >\n <Show when={!local.loading}>{local.children}</Show>\n </Button>\n );\n}\n\nexport function IconButtonLink(props: IconButtonLinkProps): JSX.Element {\n const [local, rest] = splitProps(props, [\"children\", \"class\", \"label\", \"title\", \"tooltip\", \"variant\"]);\n const tooltip = useLabelTooltip(\n () => local.tooltip,\n () => local.title ?? local.label,\n );\n\n return (\n <ButtonLink\n {...rest}\n variant={local.variant ?? \"ghost\"}\n class={`k2b-icon-button ${local.class ?? \"\"}`}\n aria-label={local.label}\n title={iconTitle(local.title, tooltip())}\n tooltip={tooltip()}\n >\n {local.children}\n </ButtonLink>\n );\n}\n"],"mappings":"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","ignoreList":[]}
@@ -13,6 +13,7 @@ var _tmpl$ = /*#__PURE__*/_$template(`<div role=group aria-haspopup=menu>`),
13
13
  _tmpl$2 = /*#__PURE__*/_$template(`<div class="k2b-ui k2b-context-menu"role=menu>`);
14
14
  import { createMemo, createSignal, createUniqueId, onCleanup, onMount, Show } from "solid-js";
15
15
  import { Portal } from "solid-js/web";
16
+ import { returnFocus, ringOnReturn } from "../internal/focus-return";
16
17
  import { useUiMessages } from "../intl/messages";
17
18
  import { DropdownItems, dropdownPosition } from "./Dropdown";
18
19
  let closeActiveContextMenu;
@@ -27,6 +28,16 @@ export function ContextMenu(props) {
27
28
  let host;
28
29
  let menu;
29
30
  let listenersAttached = false;
31
+ let hostRing = true;
32
+ let anchor = {
33
+ x: 0,
34
+ y: 0
35
+ };
36
+ let viewport = {
37
+ width: 0,
38
+ height: 0
39
+ };
40
+ let resizeObserver;
30
41
  const isOpen = () => position() !== undefined;
31
42
  const hostClass = createMemo(() => typeof props.class === "function" ? props.class(isOpen()) : props.class);
32
43
  const items = createMemo(() => props.items);
@@ -34,26 +45,45 @@ export function ContextMenu(props) {
34
45
  if (menu?.contains(event.target)) return;
35
46
  close();
36
47
  };
37
- const closeOnViewportChange = event => {
48
+ const closeOnScroll = event => {
38
49
  const target = event.target;
39
50
  if (target instanceof Node && menu?.contains(target)) return;
40
51
  close();
41
52
  };
53
+ // A resize event may leave the viewport's size unchanged; headless Chromium
54
+ // was seen to fire one on a fresh page. The clamped menu is still in place.
55
+ const closeOnResize = () => {
56
+ if (window.innerWidth === viewport.width && window.innerHeight === viewport.height) return;
57
+ close();
58
+ };
42
59
  const attachOpenListeners = () => {
43
60
  if (listenersAttached) return;
44
61
  listenersAttached = true;
45
62
  document.addEventListener("pointerdown", dismiss);
46
63
  document.addEventListener("keydown", keyDown);
47
- window.addEventListener("resize", closeOnViewportChange);
48
- window.addEventListener("scroll", closeOnViewportChange, true);
64
+ window.addEventListener("resize", closeOnResize);
65
+ window.addEventListener("scroll", closeOnScroll, true);
49
66
  };
50
67
  const detachOpenListeners = () => {
51
68
  if (!listenersAttached) return;
52
69
  listenersAttached = false;
53
70
  document.removeEventListener("pointerdown", dismiss);
54
71
  document.removeEventListener("keydown", keyDown);
55
- window.removeEventListener("resize", closeOnViewportChange);
56
- window.removeEventListener("scroll", closeOnViewportChange, true);
72
+ window.removeEventListener("resize", closeOnResize);
73
+ window.removeEventListener("scroll", closeOnScroll, true);
74
+ resizeObserver?.disconnect();
75
+ };
76
+ /** Clamps the rendered menu into the viewport beside the point that opened it. */
77
+ const place = () => {
78
+ if (!isOpen() || !menu?.isConnected) return;
79
+ const point = dropdownPosition(new DOMRect(anchor.x, anchor.y, 0, 0), menu.getBoundingClientRect(), "bottom-right", {
80
+ width: window.innerWidth,
81
+ height: window.innerHeight
82
+ }, 0);
83
+ setPosition({
84
+ x: point.left,
85
+ y: point.top
86
+ });
57
87
  };
58
88
  const close = () => {
59
89
  detachOpenListeners();
@@ -70,23 +100,33 @@ export function ContextMenu(props) {
70
100
  const open = (x, y) => {
71
101
  if (props.disabled) return;
72
102
  closeActiveContextMenu?.();
73
- const point = dropdownPosition(new DOMRect(x, y, 0, 0),
74
- // Matches the fixed 13rem `.k2b-context-menu` surface (border-box).
75
- {
76
- width: 208,
77
- height: Math.min(items().length * 38 + 12, 384)
78
- }, "bottom-right", {
103
+ // Focus may sit on a descendant of the host, as in composite widgets.
104
+ hostRing = ringOnReturn(document.activeElement);
105
+ anchor = {
106
+ x,
107
+ y
108
+ };
109
+ viewport = {
79
110
  width: window.innerWidth,
80
111
  height: window.innerHeight
81
- }, 0);
112
+ };
82
113
  setPosition({
83
- x: point.left,
84
- y: point.top
114
+ x,
115
+ y
85
116
  });
86
117
  closeActiveContextMenu = close;
87
118
  attachOpenListeners();
88
119
  props.onOpen?.();
120
+ // The menu sizes to its entries, so it is measured once rendered and
121
+ // clamped into the viewport before the next paint, and again whenever its
122
+ // entries change its size while it is open.
89
123
  queueMicrotask(() => {
124
+ if (!isOpen() || !menu?.isConnected) return;
125
+ place();
126
+ if (typeof ResizeObserver !== "undefined") {
127
+ resizeObserver ??= new ResizeObserver(place);
128
+ resizeObserver.observe(menu);
129
+ }
90
130
  focusItem(0);
91
131
  });
92
132
  };
@@ -103,7 +143,7 @@ export function ContextMenu(props) {
103
143
  } else if (event.key === "Escape") {
104
144
  event.preventDefault();
105
145
  close();
106
- host?.focus();
146
+ returnFocus(host, hostRing);
107
147
  } else if (event.key === "Tab") {
108
148
  close();
109
149
  }
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["actions/ContextMenu.tsx"],"sourcesContent":["import { createMemo, createSignal, createUniqueId, type JSX, onCleanup, onMount, Show } from \"solid-js\";\nimport { Portal } from \"solid-js/web\";\nimport { useUiMessages } from \"../intl/messages\";\nimport { type DropdownItem, DropdownItems, type DropdownPosition, dropdownPosition } from \"./Dropdown\";\n\nexport type ContextMenuProps = {\n items: readonly DropdownItem[];\n children: JSX.Element;\n class?: string | ((isOpen: boolean) => string);\n /** Tab order of the context-menu host. Descendant-focused composite widgets use -1. */\n tabIndex?: number;\n disabled?: boolean;\n onClose?: () => void;\n onOpen?: () => void;\n id?: string;\n label?: string;\n};\n\nlet closeActiveContextMenu: (() => void) | undefined;\n\nconst focusableItems = (menu: HTMLElement | undefined): HTMLElement[] =>\n menu\n ? Array.from(\n menu.querySelectorAll<HTMLElement>(\n [\n \"[role='menuitem']:not([aria-disabled='true'])\",\n \"[role='menuitemcheckbox']:not([aria-disabled='true'])\",\n \"[role='menuitemradio']:not([aria-disabled='true'])\",\n ].join(\", \"),\n ),\n )\n : [];\n\n/** Right-click menu with keyboard invocation, one-open-at-a-time behavior, and viewport clamping. */\nexport function ContextMenu(props: ContextMenuProps): JSX.Element {\n const messages = useUiMessages();\n const generatedId = `k2b-context-${createUniqueId()}`;\n const menuId = `${generatedId}-menu`;\n const [position, setPosition] = createSignal<{ x: number; y: number }>();\n let host: HTMLDivElement | undefined;\n let menu: HTMLDivElement | undefined;\n let listenersAttached = false;\n\n const isOpen = () => position() !== undefined;\n const hostClass = createMemo(() => (typeof props.class === \"function\" ? props.class(isOpen()) : props.class));\n const items = createMemo<readonly DropdownItem[]>(() => props.items);\n\n const dismiss = (event: PointerEvent) => {\n if (menu?.contains(event.target as Node)) return;\n close();\n };\n const closeOnViewportChange = (event: Event) => {\n const target = event.target;\n if (target instanceof Node && menu?.contains(target)) return;\n close();\n };\n const attachOpenListeners = () => {\n if (listenersAttached) return;\n listenersAttached = true;\n document.addEventListener(\"pointerdown\", dismiss);\n document.addEventListener(\"keydown\", keyDown);\n window.addEventListener(\"resize\", closeOnViewportChange);\n window.addEventListener(\"scroll\", closeOnViewportChange, true);\n };\n const detachOpenListeners = () => {\n if (!listenersAttached) return;\n listenersAttached = false;\n document.removeEventListener(\"pointerdown\", dismiss);\n document.removeEventListener(\"keydown\", keyDown);\n window.removeEventListener(\"resize\", closeOnViewportChange);\n window.removeEventListener(\"scroll\", closeOnViewportChange, true);\n };\n const close = () => {\n detachOpenListeners();\n if (!isOpen()) return;\n setPosition();\n if (closeActiveContextMenu === close) closeActiveContextMenu = undefined;\n props.onClose?.();\n };\n const focusItem = (index: number) => {\n const items = focusableItems(menu);\n if (items.length === 0) return;\n items[(index + items.length) % items.length]?.focus();\n };\n const open = (x: number, y: number) => {\n if (props.disabled) return;\n closeActiveContextMenu?.();\n const point = dropdownPosition(\n new DOMRect(x, y, 0, 0),\n // Matches the fixed 13rem `.k2b-context-menu` surface (border-box).\n { width: 208, height: Math.min(items().length * 38 + 12, 384) },\n \"bottom-right\" satisfies DropdownPosition,\n { width: window.innerWidth, height: window.innerHeight },\n 0,\n );\n setPosition({ x: point.left, y: point.top });\n closeActiveContextMenu = close;\n attachOpenListeners();\n props.onOpen?.();\n queueMicrotask(() => {\n focusItem(0);\n });\n };\n const keyDown = (event: KeyboardEvent) => {\n if (!isOpen()) return;\n const items = focusableItems(menu);\n const current = items.indexOf(document.activeElement as HTMLElement);\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n focusItem(current + (event.key === \"ArrowDown\" ? 1 : -1));\n } else if (event.key === \"Home\" || event.key === \"End\") {\n event.preventDefault();\n focusItem(event.key === \"Home\" ? 0 : -1);\n } else if (event.key === \"Escape\") {\n event.preventDefault();\n close();\n host?.focus();\n } else if (event.key === \"Tab\") {\n close();\n }\n };\n\n onMount(() => {\n onCleanup(() => {\n detachOpenListeners();\n if (closeActiveContextMenu === close) closeActiveContextMenu = undefined;\n });\n });\n\n return (\n <>\n {/* biome-ignore lint/a11y/useAriaPropsSupportedByRole: the named group owns the context-menu relationship without pretending arbitrary children form one button. */}\n <div\n ref={host}\n id={props.id ?? generatedId}\n class={hostClass()}\n role=\"group\"\n tabIndex={props.disabled ? undefined : (props.tabIndex ?? 0)}\n aria-label={props.label ?? messages().contextMenu}\n aria-haspopup=\"menu\"\n aria-expanded={isOpen()}\n aria-controls={isOpen() ? menuId : undefined}\n aria-disabled={props.disabled ? \"true\" : undefined}\n onContextMenu={(event) => {\n if (props.disabled) return;\n event.preventDefault();\n event.stopPropagation();\n open(event.clientX, event.clientY);\n }}\n onKeyDown={(event) => {\n if (props.disabled || (event.key !== \"ContextMenu\" && !(event.shiftKey && event.key === \"F10\"))) return;\n event.preventDefault();\n const rect = host?.getBoundingClientRect();\n if (rect) open(rect.left + 12, rect.top + 12);\n }}\n >\n {props.children}\n </div>\n <Show when={position()}>\n {(point) => (\n <Portal>\n <div\n ref={menu}\n id={menuId}\n class=\"k2b-ui k2b-context-menu\"\n role=\"menu\"\n aria-label={props.label ?? messages().contextMenu}\n style={{ left: `${point().x}px`, top: `${point().y}px` }}\n onPointerDown={(event) => event.stopPropagation()}\n >\n <DropdownItems items={items()} close={close} />\n </div>\n </Portal>\n )}\n </Show>\n </>\n );\n}\n\nexport default ContextMenu;\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["actions/ContextMenu.tsx"],"sourcesContent":["import { createMemo, createSignal, createUniqueId, type JSX, onCleanup, onMount, Show } from \"solid-js\";\nimport { Portal } from \"solid-js/web\";\nimport { returnFocus, ringOnReturn } from \"../internal/focus-return\";\nimport { useUiMessages } from \"../intl/messages\";\nimport { type DropdownItem, DropdownItems, type DropdownPosition, dropdownPosition } from \"./Dropdown\";\n\nexport type ContextMenuProps = {\n items: readonly DropdownItem[];\n children: JSX.Element;\n class?: string | ((isOpen: boolean) => string);\n /** Tab order of the context-menu host. Descendant-focused composite widgets use -1. */\n tabIndex?: number;\n disabled?: boolean;\n onClose?: () => void;\n onOpen?: () => void;\n id?: string;\n label?: string;\n};\n\nlet closeActiveContextMenu: (() => void) | undefined;\n\nconst focusableItems = (menu: HTMLElement | undefined): HTMLElement[] =>\n menu\n ? Array.from(\n menu.querySelectorAll<HTMLElement>(\n [\n \"[role='menuitem']:not([aria-disabled='true'])\",\n \"[role='menuitemcheckbox']:not([aria-disabled='true'])\",\n \"[role='menuitemradio']:not([aria-disabled='true'])\",\n ].join(\", \"),\n ),\n )\n : [];\n\n/** Right-click menu with keyboard invocation, one-open-at-a-time behavior, and viewport clamping. */\nexport function ContextMenu(props: ContextMenuProps): JSX.Element {\n const messages = useUiMessages();\n const generatedId = `k2b-context-${createUniqueId()}`;\n const menuId = `${generatedId}-menu`;\n const [position, setPosition] = createSignal<{ x: number; y: number }>();\n let host: HTMLDivElement | undefined;\n let menu: HTMLDivElement | undefined;\n let listenersAttached = false;\n let hostRing = true;\n let anchor = { x: 0, y: 0 };\n let viewport = { width: 0, height: 0 };\n let resizeObserver: ResizeObserver | undefined;\n\n const isOpen = () => position() !== undefined;\n const hostClass = createMemo(() => (typeof props.class === \"function\" ? props.class(isOpen()) : props.class));\n const items = createMemo<readonly DropdownItem[]>(() => props.items);\n\n const dismiss = (event: PointerEvent) => {\n if (menu?.contains(event.target as Node)) return;\n close();\n };\n const closeOnScroll = (event: Event) => {\n const target = event.target;\n if (target instanceof Node && menu?.contains(target)) return;\n close();\n };\n // A resize event may leave the viewport's size unchanged; headless Chromium\n // was seen to fire one on a fresh page. The clamped menu is still in place.\n const closeOnResize = () => {\n if (window.innerWidth === viewport.width && window.innerHeight === viewport.height) return;\n close();\n };\n const attachOpenListeners = () => {\n if (listenersAttached) return;\n listenersAttached = true;\n document.addEventListener(\"pointerdown\", dismiss);\n document.addEventListener(\"keydown\", keyDown);\n window.addEventListener(\"resize\", closeOnResize);\n window.addEventListener(\"scroll\", closeOnScroll, true);\n };\n const detachOpenListeners = () => {\n if (!listenersAttached) return;\n listenersAttached = false;\n document.removeEventListener(\"pointerdown\", dismiss);\n document.removeEventListener(\"keydown\", keyDown);\n window.removeEventListener(\"resize\", closeOnResize);\n window.removeEventListener(\"scroll\", closeOnScroll, true);\n resizeObserver?.disconnect();\n };\n /** Clamps the rendered menu into the viewport beside the point that opened it. */\n const place = () => {\n if (!isOpen() || !menu?.isConnected) return;\n const point = dropdownPosition(\n new DOMRect(anchor.x, anchor.y, 0, 0),\n menu.getBoundingClientRect(),\n \"bottom-right\" satisfies DropdownPosition,\n { width: window.innerWidth, height: window.innerHeight },\n 0,\n );\n setPosition({ x: point.left, y: point.top });\n };\n const close = () => {\n detachOpenListeners();\n if (!isOpen()) return;\n setPosition();\n if (closeActiveContextMenu === close) closeActiveContextMenu = undefined;\n props.onClose?.();\n };\n const focusItem = (index: number) => {\n const items = focusableItems(menu);\n if (items.length === 0) return;\n items[(index + items.length) % items.length]?.focus();\n };\n const open = (x: number, y: number) => {\n if (props.disabled) return;\n closeActiveContextMenu?.();\n // Focus may sit on a descendant of the host, as in composite widgets.\n hostRing = ringOnReturn(document.activeElement);\n anchor = { x, y };\n viewport = { width: window.innerWidth, height: window.innerHeight };\n setPosition({ x, y });\n closeActiveContextMenu = close;\n attachOpenListeners();\n props.onOpen?.();\n // The menu sizes to its entries, so it is measured once rendered and\n // clamped into the viewport before the next paint, and again whenever its\n // entries change its size while it is open.\n queueMicrotask(() => {\n if (!isOpen() || !menu?.isConnected) return;\n place();\n if (typeof ResizeObserver !== \"undefined\") {\n resizeObserver ??= new ResizeObserver(place);\n resizeObserver.observe(menu);\n }\n focusItem(0);\n });\n };\n const keyDown = (event: KeyboardEvent) => {\n if (!isOpen()) return;\n const items = focusableItems(menu);\n const current = items.indexOf(document.activeElement as HTMLElement);\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n focusItem(current + (event.key === \"ArrowDown\" ? 1 : -1));\n } else if (event.key === \"Home\" || event.key === \"End\") {\n event.preventDefault();\n focusItem(event.key === \"Home\" ? 0 : -1);\n } else if (event.key === \"Escape\") {\n event.preventDefault();\n close();\n returnFocus(host, hostRing);\n } else if (event.key === \"Tab\") {\n close();\n }\n };\n\n onMount(() => {\n onCleanup(() => {\n detachOpenListeners();\n if (closeActiveContextMenu === close) closeActiveContextMenu = undefined;\n });\n });\n\n return (\n <>\n {/* biome-ignore lint/a11y/useAriaPropsSupportedByRole: the named group owns the context-menu relationship without pretending arbitrary children form one button. */}\n <div\n ref={host}\n id={props.id ?? generatedId}\n class={hostClass()}\n role=\"group\"\n tabIndex={props.disabled ? undefined : (props.tabIndex ?? 0)}\n aria-label={props.label ?? messages().contextMenu}\n aria-haspopup=\"menu\"\n aria-expanded={isOpen()}\n aria-controls={isOpen() ? menuId : undefined}\n aria-disabled={props.disabled ? \"true\" : undefined}\n onContextMenu={(event) => {\n if (props.disabled) return;\n event.preventDefault();\n event.stopPropagation();\n open(event.clientX, event.clientY);\n }}\n onKeyDown={(event) => {\n if (props.disabled || (event.key !== \"ContextMenu\" && !(event.shiftKey && event.key === \"F10\"))) return;\n event.preventDefault();\n const rect = host?.getBoundingClientRect();\n if (rect) open(rect.left + 12, rect.top + 12);\n }}\n >\n {props.children}\n </div>\n <Show when={position()}>\n {(point) => (\n <Portal>\n <div\n ref={menu}\n id={menuId}\n class=\"k2b-ui k2b-context-menu\"\n role=\"menu\"\n aria-label={props.label ?? messages().contextMenu}\n style={{ left: `${point().x}px`, top: `${point().y}px` }}\n onPointerDown={(event) => event.stopPropagation()}\n >\n <DropdownItems items={items()} close={close} />\n </div>\n </Portal>\n )}\n </Show>\n </>\n );\n}\n\nexport default ContextMenu;\n"],"mappings":"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","ignoreList":[]}
@@ -31,9 +31,10 @@ var _tmpl$ = /*#__PURE__*/_$template(`<span class=k2b-dropdown__copy><span></spa
31
31
  _tmpl$12 = /*#__PURE__*/_$template(`<button>`),
32
32
  _tmpl$13 = /*#__PURE__*/_$template(`<span><!$><!/><div popover=auto role=menu>`);
33
33
  import { createContext, createEffect, createSignal, createUniqueId, For, onCleanup, onMount, Show, splitProps, useContext } from "solid-js";
34
- import { Tooltip } from "../feedback/Tooltip";
34
+ import { Tooltip, useLabelTooltip } from "../feedback/Tooltip";
35
+ import { returnFocus, ringOnReturn } from "../internal/focus-return";
35
36
  import { useUiMessages } from "../intl/messages";
36
- import { Button } from "./Button";
37
+ import { Button, iconTitle } from "./Button";
37
38
 
38
39
  // Resolved outside JSX: a conditional attribute compiles to a lazy memo, which would be created inside the click handler.
39
40
  const selectAction = item => item.action;
@@ -256,10 +257,12 @@ function DropdownChoiceItem(props) {
256
257
  }), _el$14, _co$2);
257
258
  _$insert(_el$0, _$createComponent(Show, {
258
259
  get when() {
259
- return _$memo(() => props.choice === "radio")() && checked();
260
+ return props.choice === "radio";
260
261
  },
261
262
  get children() {
262
- return _$getNextElement(_tmpl$9);
263
+ var _el$15 = _$getNextElement(_tmpl$9);
264
+ _$effect(() => _$setAttribute(_el$15, "data-hidden", checked() ? undefined : "true"));
265
+ return _el$15;
263
266
  }
264
267
  }), _el$23, _co$6);
265
268
  _$effect(_p$ => {
@@ -383,9 +386,11 @@ export function DropdownItems(props) {
383
386
  function DropdownTrigger(props) {
384
387
  const context = useContext(DropdownContext);
385
388
  if (!context) throw new Error("Dropdown.Trigger must be used inside Dropdown.Root");
386
- const [local, rest] = splitProps(props, ["appearance", "children", "class", "disabled", "iconOnly", "label", "ref", "size", "tabIndex", "tooltip", "tooltipDelay", "tooltipPlacement", "type", "variant"]);
389
+ const [local, rest] = splitProps(props, ["appearance", "children", "class", "disabled", "iconOnly", "label", "ref", "size", "tabIndex", "title", "tooltip", "tooltipDelay", "tooltipPlacement", "type", "variant"]);
387
390
  let target;
388
391
  const disabled = () => Boolean(context.disabled() || local.disabled);
392
+ // An icon-only trigger shows its label as a hint, like IconButton.
393
+ const tooltip = useLabelTooltip(() => local.tooltip, () => local.iconOnly ? local.title ?? local.label : undefined);
389
394
  const register = element => {
390
395
  target = element;
391
396
  context.registerTrigger(element);
@@ -430,8 +435,11 @@ function DropdownTrigger(props) {
430
435
  get variant() {
431
436
  return local.variant ?? (local.iconOnly ? "ghost" : undefined);
432
437
  },
438
+ get title() {
439
+ return iconTitle(local.title, tooltip());
440
+ },
433
441
  get tooltip() {
434
- return local.tooltip;
442
+ return tooltip();
435
443
  },
436
444
  get tooltipDelay() {
437
445
  return local.tooltipDelay;
@@ -473,6 +481,9 @@ function DropdownTrigger(props) {
473
481
  },
474
482
  get ["class"]() {
475
483
  return `k2b-dropdown__trigger ${local.class ?? ""}`;
484
+ },
485
+ get title() {
486
+ return iconTitle(local.title, tooltip());
476
487
  }
477
488
  }), false, true);
478
489
  _$insert(_el$34, () => local.children);
@@ -480,12 +491,12 @@ function DropdownTrigger(props) {
480
491
  return _el$34;
481
492
  })(), _$createComponent(Show, {
482
493
  get when() {
483
- return _$memo(() => local.tooltip !== false)() && local.tooltip !== undefined;
494
+ return _$memo(() => tooltip() !== false)() && tooltip() !== undefined;
484
495
  },
485
496
  get children() {
486
497
  return _$createComponent(Tooltip, {
487
498
  get content() {
488
- return local.tooltip;
499
+ return tooltip();
489
500
  },
490
501
  target: () => target,
491
502
  get delay() {
@@ -510,8 +521,10 @@ function DropdownRoot(props) {
510
521
  const [internalOpen, setInternalOpen] = createSignal(false);
511
522
  let triggerRef;
512
523
  let menuRef;
524
+ let triggerRing = true;
513
525
  let mounted = false;
514
526
  let viewportListenersAttached = false;
527
+ let resizeObserver;
515
528
  const isOpen = () => props.open ?? internalOpen();
516
529
  const position = () => typeof props.position === "function" ? props.position() : props.position ?? (props.align === "end" ? "bottom-left" : "bottom-right");
517
530
  const place = () => {
@@ -531,20 +544,28 @@ function DropdownRoot(props) {
531
544
  viewportListenersAttached = true;
532
545
  window.addEventListener("resize", reposition);
533
546
  window.addEventListener("scroll", reposition, true);
547
+ // A content-sized menu changes size when its items change while it is open.
548
+ if (menuRef && typeof ResizeObserver !== "undefined") {
549
+ resizeObserver ??= new ResizeObserver(reposition);
550
+ resizeObserver.observe(menuRef);
551
+ }
534
552
  };
535
553
  const detachViewportListeners = () => {
536
554
  if (!viewportListenersAttached) return;
537
555
  viewportListenersAttached = false;
538
556
  window.removeEventListener("resize", reposition);
539
557
  window.removeEventListener("scroll", reposition, true);
558
+ resizeObserver?.disconnect();
540
559
  };
541
560
  const close = (restoreFocus = true) => {
561
+ // Before hiding, so the popover does not return focus on its own.
562
+ if (restoreFocus) returnFocus(triggerRef, triggerRing);
542
563
  if (menuRef?.matches(":popover-open")) menuRef.hidePopover();
543
564
  detachViewportListeners();
544
- if (restoreFocus) queueMicrotask(() => triggerRef?.focus());
545
565
  };
546
566
  const open = (focus = "first") => {
547
567
  if (props.disabled || !menuRef || menuRef.matches(":popover-open")) return;
568
+ triggerRing = ringOnReturn(triggerRef);
548
569
  menuRef.showPopover();
549
570
  // The toggle event arrives a task later; expose the open state before focus moves into the menu.
550
571
  setInternalOpen(true);
@@ -573,8 +594,8 @@ function DropdownRoot(props) {
573
594
  focusMenuItem(menuRef, event.key === "Home" ? 0 : -1);
574
595
  } else if (event.key === "Escape") {
575
596
  event.preventDefault();
597
+ returnFocus(triggerRef, triggerRing);
576
598
  requestOpen(false);
577
- queueMicrotask(() => triggerRef?.focus());
578
599
  } else if (event.key === "Tab") {
579
600
  requestOpen(false);
580
601
  }
@@ -646,14 +667,16 @@ function DropdownRoot(props) {
646
667
  _v$19 = props.width ? {
647
668
  "--k2b-dropdown-width": props.width
648
669
  } : undefined,
649
- _v$20 = props.variant ?? "default",
650
- _v$21 = position();
670
+ _v$20 = props.width ? "fixed" : undefined,
671
+ _v$21 = props.variant ?? "default",
672
+ _v$22 = position();
651
673
  _v$16 !== _p$.e && _$className(_el$35, _p$.e = _v$16);
652
674
  _v$17 !== _p$.t && _$setAttribute(_el$36, "aria-label", _p$.t = _v$17);
653
675
  _v$18 !== _p$.a && _$className(_el$36, _p$.a = _v$18);
654
676
  _p$.o = _$style(_el$36, _v$19, _p$.o);
655
- _v$20 !== _p$.i && _$setAttribute(_el$36, "data-variant", _p$.i = _v$20);
656
- _v$21 !== _p$.n && _$setAttribute(_el$36, "data-position", _p$.n = _v$21);
677
+ _v$20 !== _p$.i && _$setAttribute(_el$36, "data-width", _p$.i = _v$20);
678
+ _v$21 !== _p$.n && _$setAttribute(_el$36, "data-variant", _p$.n = _v$21);
679
+ _v$22 !== _p$.s && _$setAttribute(_el$36, "data-position", _p$.s = _v$22);
657
680
  return _p$;
658
681
  }, {
659
682
  e: undefined,
@@ -661,7 +684,8 @@ function DropdownRoot(props) {
661
684
  a: undefined,
662
685
  o: undefined,
663
686
  i: undefined,
664
- n: undefined
687
+ n: undefined,
688
+ s: undefined
665
689
  });
666
690
  _$runHydrationEvents();
667
691
  return _el$35;