@k2b/ui 0.29.0 → 0.31.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 (243) hide show
  1. package/README.md +2 -2
  2. package/dist/base.css +2 -2
  3. package/dist/browser/actions/Dropdown.js +6 -3
  4. package/dist/browser/actions/Dropdown.js.map +1 -1
  5. package/dist/browser/actions/FilterChip.js +137 -7
  6. package/dist/browser/actions/FilterChip.js.map +1 -1
  7. package/dist/browser/actions/SegmentedControl.js +5 -9
  8. package/dist/browser/actions/SegmentedControl.js.map +1 -1
  9. package/dist/browser/actions/SpotlightSearch.js +6 -3
  10. package/dist/browser/actions/SpotlightSearch.js.map +1 -1
  11. package/dist/browser/actions/Tabs.js +5 -2
  12. package/dist/browser/actions/Tabs.js.map +1 -1
  13. package/dist/browser/actions/Toolbar.js +2 -0
  14. package/dist/browser/actions/Toolbar.js.map +1 -1
  15. package/dist/browser/actions/ViewTabs.js +294 -0
  16. package/dist/browser/actions/ViewTabs.js.map +1 -0
  17. package/dist/browser/actions/filter-messages.js +6 -2
  18. package/dist/browser/actions/filter-messages.js.map +1 -1
  19. package/dist/browser/actions/index.js +2 -1
  20. package/dist/browser/actions/index.js.map +1 -1
  21. package/dist/browser/chat/ChatComposer.js +1108 -604
  22. package/dist/browser/chat/ChatComposer.js.map +1 -1
  23. package/dist/browser/chat/ChatDecision.js +146 -84
  24. package/dist/browser/chat/ChatDecision.js.map +1 -1
  25. package/dist/browser/chat/ChatPrimitives.js +292 -159
  26. package/dist/browser/chat/ChatPrimitives.js.map +1 -1
  27. package/dist/browser/chat/chat-messages.js +8 -0
  28. package/dist/browser/chat/chat-messages.js.map +1 -1
  29. package/dist/browser/chat/command-search.js +26 -15
  30. package/dist/browser/chat/command-search.js.map +1 -1
  31. package/dist/browser/chat/index.js +4 -2
  32. package/dist/browser/chat/index.js.map +1 -1
  33. package/dist/browser/content/Calendar.js +143 -116
  34. package/dist/browser/content/Calendar.js.map +1 -1
  35. package/dist/browser/content/CalendarWeekView.js +291 -266
  36. package/dist/browser/content/CalendarWeekView.js.map +1 -1
  37. package/dist/browser/content/DataTable.js +224 -155
  38. package/dist/browser/content/DataTable.js.map +1 -1
  39. package/dist/browser/content/MapAttribution.js +27 -0
  40. package/dist/browser/content/MapAttribution.js.map +1 -0
  41. package/dist/browser/content/MapView.js +553 -0
  42. package/dist/browser/content/MapView.js.map +1 -0
  43. package/dist/browser/content/MapViewFrame.js +395 -0
  44. package/dist/browser/content/MapViewFrame.js.map +1 -0
  45. package/dist/browser/content/data-table-messages.js +4 -2
  46. package/dist/browser/content/data-table-messages.js.map +1 -1
  47. package/dist/browser/content/index.js +5 -0
  48. package/dist/browser/content/index.js.map +1 -1
  49. package/dist/browser/content/map-palette.js +39 -0
  50. package/dist/browser/content/map-palette.js.map +1 -0
  51. package/dist/browser/content/map-style.js +276 -0
  52. package/dist/browser/content/map-style.js.map +1 -0
  53. package/dist/browser/content/map-view-geometry.js +269 -0
  54. package/dist/browser/content/map-view-geometry.js.map +1 -0
  55. package/dist/browser/content/map-view-live.js +28 -0
  56. package/dist/browser/content/map-view-live.js.map +1 -0
  57. package/dist/browser/content/map-view-messages.js +53 -0
  58. package/dist/browser/content/map-view-messages.js.map +1 -0
  59. package/dist/browser/content/map-view-types.js +2 -0
  60. package/dist/browser/content/map-view-types.js.map +1 -0
  61. package/dist/browser/content/map-view-worker.js +10 -0
  62. package/dist/browser/feedback/MovedHint.js +197 -0
  63. package/dist/browser/feedback/MovedHint.js.map +1 -0
  64. package/dist/browser/feedback/dialog-core.js +85 -9
  65. package/dist/browser/feedback/dialog-core.js.map +1 -1
  66. package/dist/browser/feedback/index.js +1 -0
  67. package/dist/browser/feedback/index.js.map +1 -1
  68. package/dist/browser/feedback/prompts.js +6 -1
  69. package/dist/browser/feedback/prompts.js.map +1 -1
  70. package/dist/browser/inputs/DatePicker.js +337 -212
  71. package/dist/browser/inputs/DatePicker.js.map +1 -1
  72. package/dist/browser/inputs/date-picker.js +59 -5
  73. package/dist/browser/inputs/date-picker.js.map +1 -1
  74. package/dist/browser/intl/messages.js +12 -0
  75. package/dist/browser/intl/messages.js.map +1 -1
  76. package/dist/browser/intl/weekend.js +39 -0
  77. package/dist/browser/intl/weekend.js.map +1 -0
  78. package/dist/browser/layout/AppOverview.js +0 -1
  79. package/dist/browser/layout/AppOverview.js.map +1 -1
  80. package/dist/browser/layout/AppWorkspace.js +701 -476
  81. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  82. package/dist/browser/layout/PageTitle.js +3 -2
  83. package/dist/browser/layout/PageTitle.js.map +1 -1
  84. package/dist/browser/layout/SettingsModal.js +2 -2
  85. package/dist/browser/layout/SettingsModal.js.map +1 -1
  86. package/dist/browser/layout/SidebarSwitcher.js +13 -12
  87. package/dist/browser/layout/SidebarSwitcher.js.map +1 -1
  88. package/dist/browser/layout/TabBar.js +1 -1
  89. package/dist/browser/layout/TabBar.js.map +1 -1
  90. package/dist/browser/layout/app-host-width.js +44 -0
  91. package/dist/browser/layout/app-host-width.js.map +1 -0
  92. package/dist/browser/layout/app-workspace-controller.js +331 -45
  93. package/dist/browser/layout/app-workspace-controller.js.map +1 -1
  94. package/dist/browser/layout/app-workspace-width.js +26 -0
  95. package/dist/browser/layout/app-workspace-width.js.map +1 -0
  96. package/dist/browser/layout/index.js +2 -1
  97. package/dist/browser/layout/index.js.map +1 -1
  98. package/dist/browser/surfaces/Placeholder.js +36 -13
  99. package/dist/browser/surfaces/Placeholder.js.map +1 -1
  100. package/dist/licenses/maplibre-gl.txt +116 -0
  101. package/dist/ssr/actions/Dropdown.js +5 -2
  102. package/dist/ssr/actions/Dropdown.js.map +1 -1
  103. package/dist/ssr/actions/FilterChip.js +59 -7
  104. package/dist/ssr/actions/FilterChip.js.map +1 -1
  105. package/dist/ssr/actions/SegmentedControl.js +1 -2
  106. package/dist/ssr/actions/SegmentedControl.js.map +1 -1
  107. package/dist/ssr/actions/SpotlightSearch.js +2 -2
  108. package/dist/ssr/actions/SpotlightSearch.js.map +1 -1
  109. package/dist/ssr/actions/Tabs.js +2 -2
  110. package/dist/ssr/actions/Tabs.js.map +1 -1
  111. package/dist/ssr/actions/Toolbar.js +2 -0
  112. package/dist/ssr/actions/Toolbar.js.map +1 -1
  113. package/dist/ssr/actions/ViewTabs.js +211 -0
  114. package/dist/ssr/actions/ViewTabs.js.map +1 -0
  115. package/dist/ssr/actions/filter-messages.js +6 -2
  116. package/dist/ssr/actions/filter-messages.js.map +1 -1
  117. package/dist/ssr/actions/index.js +2 -1
  118. package/dist/ssr/actions/index.js.map +1 -1
  119. package/dist/ssr/chat/ChatComposer.js +546 -131
  120. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  121. package/dist/ssr/chat/ChatDecision.js +44 -19
  122. package/dist/ssr/chat/ChatDecision.js.map +1 -1
  123. package/dist/ssr/chat/ChatPrimitives.js +95 -24
  124. package/dist/ssr/chat/ChatPrimitives.js.map +1 -1
  125. package/dist/ssr/chat/chat-messages.js +8 -0
  126. package/dist/ssr/chat/chat-messages.js.map +1 -1
  127. package/dist/ssr/chat/command-search.js +26 -15
  128. package/dist/ssr/chat/command-search.js.map +1 -1
  129. package/dist/ssr/chat/index.js +4 -2
  130. package/dist/ssr/chat/index.js.map +1 -1
  131. package/dist/ssr/content/Calendar.js +109 -100
  132. package/dist/ssr/content/Calendar.js.map +1 -1
  133. package/dist/ssr/content/CalendarWeekView.js +42 -28
  134. package/dist/ssr/content/CalendarWeekView.js.map +1 -1
  135. package/dist/ssr/content/DataTable.js +78 -31
  136. package/dist/ssr/content/DataTable.js.map +1 -1
  137. package/dist/ssr/content/MapAttribution.js +19 -0
  138. package/dist/ssr/content/MapAttribution.js.map +1 -0
  139. package/dist/ssr/content/MapView.js +474 -0
  140. package/dist/ssr/content/MapView.js.map +1 -0
  141. package/dist/ssr/content/MapViewFrame.js +193 -0
  142. package/dist/ssr/content/MapViewFrame.js.map +1 -0
  143. package/dist/ssr/content/data-table-messages.js +4 -2
  144. package/dist/ssr/content/data-table-messages.js.map +1 -1
  145. package/dist/ssr/content/index.js +5 -0
  146. package/dist/ssr/content/index.js.map +1 -1
  147. package/dist/ssr/content/map-palette.js +39 -0
  148. package/dist/ssr/content/map-palette.js.map +1 -0
  149. package/dist/ssr/content/map-style.js +276 -0
  150. package/dist/ssr/content/map-style.js.map +1 -0
  151. package/dist/ssr/content/map-view-geometry.js +269 -0
  152. package/dist/ssr/content/map-view-geometry.js.map +1 -0
  153. package/dist/ssr/content/map-view-live.js +1 -0
  154. package/dist/ssr/content/map-view-messages.js +53 -0
  155. package/dist/ssr/content/map-view-messages.js.map +1 -0
  156. package/dist/ssr/content/map-view-types.js +2 -0
  157. package/dist/ssr/content/map-view-types.js.map +1 -0
  158. package/dist/ssr/feedback/MovedHint.js +166 -0
  159. package/dist/ssr/feedback/MovedHint.js.map +1 -0
  160. package/dist/ssr/feedback/dialog-core.js +85 -9
  161. package/dist/ssr/feedback/dialog-core.js.map +1 -1
  162. package/dist/ssr/feedback/index.js +1 -0
  163. package/dist/ssr/feedback/index.js.map +1 -1
  164. package/dist/ssr/feedback/prompts.js +6 -1
  165. package/dist/ssr/feedback/prompts.js.map +1 -1
  166. package/dist/ssr/inputs/DatePicker.js +138 -69
  167. package/dist/ssr/inputs/DatePicker.js.map +1 -1
  168. package/dist/ssr/inputs/date-picker.js +59 -5
  169. package/dist/ssr/inputs/date-picker.js.map +1 -1
  170. package/dist/ssr/intl/messages.js +12 -0
  171. package/dist/ssr/intl/messages.js.map +1 -1
  172. package/dist/ssr/intl/weekend.js +39 -0
  173. package/dist/ssr/intl/weekend.js.map +1 -0
  174. package/dist/ssr/layout/AppOverview.js +0 -1
  175. package/dist/ssr/layout/AppOverview.js.map +1 -1
  176. package/dist/ssr/layout/AppWorkspace.js +216 -44
  177. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  178. package/dist/ssr/layout/PageTitle.js +3 -2
  179. package/dist/ssr/layout/PageTitle.js.map +1 -1
  180. package/dist/ssr/layout/SettingsModal.js +2 -2
  181. package/dist/ssr/layout/SettingsModal.js.map +1 -1
  182. package/dist/ssr/layout/SidebarSwitcher.js +13 -12
  183. package/dist/ssr/layout/SidebarSwitcher.js.map +1 -1
  184. package/dist/ssr/layout/TabBar.js +1 -1
  185. package/dist/ssr/layout/TabBar.js.map +1 -1
  186. package/dist/ssr/layout/app-host-width.js +44 -0
  187. package/dist/ssr/layout/app-host-width.js.map +1 -0
  188. package/dist/ssr/layout/app-workspace-controller.js +331 -45
  189. package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
  190. package/dist/ssr/layout/app-workspace-width.js +26 -0
  191. package/dist/ssr/layout/app-workspace-width.js.map +1 -0
  192. package/dist/ssr/layout/index.js +2 -1
  193. package/dist/ssr/layout/index.js.map +1 -1
  194. package/dist/ssr/surfaces/Placeholder.js +24 -7
  195. package/dist/ssr/surfaces/Placeholder.js.map +1 -1
  196. package/dist/styles.css +1 -1
  197. package/dist/types/actions/Dropdown.d.ts +4 -0
  198. package/dist/types/actions/FilterChip.d.ts +22 -0
  199. package/dist/types/actions/ViewTabs.d.ts +30 -0
  200. package/dist/types/actions/filter-messages.d.ts +2 -0
  201. package/dist/types/actions/index.d.ts +4 -2
  202. package/dist/types/chat/ChatComposer.d.ts +52 -0
  203. package/dist/types/chat/ChatDecision.d.ts +18 -2
  204. package/dist/types/chat/ChatPrimitives.d.ts +10 -0
  205. package/dist/types/chat/chat-messages.d.ts +4 -0
  206. package/dist/types/chat/command-search.d.ts +2 -0
  207. package/dist/types/chat/index.d.ts +9 -8
  208. package/dist/types/content/DataTable.d.ts +3 -0
  209. package/dist/types/content/MapAttribution.d.ts +6 -0
  210. package/dist/types/content/MapView.d.ts +5 -0
  211. package/dist/types/content/MapViewFrame.d.ts +31 -0
  212. package/dist/types/content/data-table-messages.d.ts +1 -0
  213. package/dist/types/content/index.d.ts +6 -0
  214. package/dist/types/content/map-palette.d.ts +58 -0
  215. package/dist/types/content/map-style.d.ts +29 -0
  216. package/dist/types/content/map-view-geometry.d.ts +55 -0
  217. package/dist/types/content/map-view-live.d.ts +2 -0
  218. package/dist/types/content/map-view-messages.d.ts +21 -0
  219. package/dist/types/content/map-view-types.d.ts +61 -0
  220. package/dist/types/feedback/MovedHint.d.ts +25 -0
  221. package/dist/types/feedback/dialog-core.d.ts +6 -2
  222. package/dist/types/feedback/index.d.ts +2 -0
  223. package/dist/types/feedback/prompts.d.ts +2 -0
  224. package/dist/types/inputs/DatePicker.d.ts +7 -0
  225. package/dist/types/inputs/date-picker.d.ts +25 -1
  226. package/dist/types/intl/messages.d.ts +6 -0
  227. package/dist/types/intl/weekend.d.ts +12 -0
  228. package/dist/types/layout/AppWorkspace.d.ts +48 -8
  229. package/dist/types/layout/app-host-width.d.ts +6 -0
  230. package/dist/types/layout/app-workspace-width.d.ts +13 -0
  231. package/dist/types/layout/index.d.ts +3 -2
  232. package/dist/types/surfaces/Placeholder.d.ts +2 -1
  233. package/map-glyphs/IBM Plex Sans Medium/0-255.pbf +0 -0
  234. package/map-glyphs/IBM Plex Sans Medium/1024-1279.pbf +0 -0
  235. package/map-glyphs/IBM Plex Sans Medium/256-511.pbf +0 -0
  236. package/map-glyphs/IBM Plex Sans Medium/512-767.pbf +0 -0
  237. package/map-glyphs/IBM Plex Sans Medium/768-1023.pbf +0 -0
  238. package/map-glyphs/IBM Plex Sans Medium/7680-7935.pbf +0 -0
  239. package/map-glyphs/IBM Plex Sans Medium/8192-8447.pbf +0 -0
  240. package/map-glyphs/IBM Plex Sans Medium/8448-8703.pbf +0 -0
  241. package/map-glyphs/IBM Plex Sans Medium/8704-8959.pbf +0 -0
  242. package/map-glyphs/OFL.txt +93 -0
  243. package/package.json +11 -6
@@ -0,0 +1,197 @@
1
+ import { template as _$template } from "solid-js/web";
2
+ import { className as _$className } from "solid-js/web";
3
+ import { effect as _$effect } from "solid-js/web";
4
+ import { getNextElement as _$getNextElement } from "solid-js/web";
5
+ import { getNextMarker as _$getNextMarker } from "solid-js/web";
6
+ import { createComponent as _$createComponent } from "solid-js/web";
7
+ import { insert as _$insert } from "solid-js/web";
8
+ import { setAttribute as _$setAttribute } from "solid-js/web";
9
+ import { use as _$use } from "solid-js/web";
10
+ var _tmpl$ = /*#__PURE__*/_$template(`<div popover=manual role=group><p class=k2b-moved-hint__text></p><div class=k2b-moved-hint__actions><!$><!/><!$><!/>`);
11
+ import { createEffect, createSignal, createUniqueId, onCleanup, onMount, Show } from "solid-js";
12
+ import { Button, ButtonLink } from "../actions/Button";
13
+ import { useUiMessages } from "../intl/messages";
14
+ import { announce } from "./announce";
15
+ import { positionTooltipSurface } from "./tooltip-position";
16
+ export function MovedHint(props) {
17
+ const messages = useUiMessages();
18
+ const textId = `k2b-moved-hint-${createUniqueId()}`;
19
+ const [mounted, setMounted] = createSignal(false);
20
+ const [dismissed, setDismissed] = createSignal(false);
21
+ let surface;
22
+ let text;
23
+ let previousFocus;
24
+ let shown = false;
25
+ let announced = false;
26
+ const hide = () => {
27
+ if (!shown) return;
28
+ shown = false;
29
+ try {
30
+ surface?.hidePopover?.();
31
+ } catch {
32
+ // Cleanup can run after the surface disconnects.
33
+ }
34
+ };
35
+ const dismiss = restoreFocus => {
36
+ if (!props.open || dismissed()) return false;
37
+ const focusInside = surface?.contains(document.activeElement);
38
+ const target = props.target();
39
+ const returnTo = previousFocus?.isConnected && !previousFocus.matches(":disabled") ? previousFocus : target && (target.tabIndex >= 0 || target.hasAttribute("tabindex")) && !target.matches(":disabled") ? target : undefined;
40
+ hide();
41
+ if (restoreFocus && focusInside && returnTo) {
42
+ returnTo.focus({
43
+ preventScroll: true
44
+ });
45
+ }
46
+ setDismissed(true);
47
+ props.onDismiss();
48
+ return true;
49
+ };
50
+ createEffect(() => {
51
+ if (!props.open) {
52
+ setDismissed(false);
53
+ previousFocus = undefined;
54
+ }
55
+ });
56
+ onMount(() => setMounted(true));
57
+ createEffect(() => {
58
+ if (!mounted() || !props.open || dismissed()) return;
59
+ const anchor = props.target();
60
+ const placement = props.placement ?? "bottom";
61
+ if (!surface || !anchor) return;
62
+
63
+ // A target scrolled out of view, or clipped by its scroller, takes the hint along instead of leaving it
64
+ // pinned to the viewport edge over unrelated content.
65
+ let inView = typeof IntersectionObserver === "undefined";
66
+ const position = () => {
67
+ if (!props.open || dismissed() || !inView || !anchor.isConnected || anchor.getClientRects().length === 0) {
68
+ hide();
69
+ return;
70
+ }
71
+ if (!surface || typeof surface.showPopover !== "function") return;
72
+ if (!shown) {
73
+ try {
74
+ surface.showPopover();
75
+ shown = true;
76
+ } catch {
77
+ return;
78
+ }
79
+ }
80
+ positionTooltipSurface(surface, anchor, placement);
81
+ if (!announced) {
82
+ announced = true;
83
+ announce(text?.textContent ?? "");
84
+ }
85
+ };
86
+ const focusIn = event => {
87
+ if (!(event.relatedTarget instanceof Node) || !surface?.contains(event.relatedTarget)) {
88
+ previousFocus = event.relatedTarget instanceof HTMLElement ? event.relatedTarget : undefined;
89
+ }
90
+ };
91
+ const keydown = event => {
92
+ if (event.defaultPrevented || event.isComposing) return;
93
+ if (event.key !== "Escape" || !(event.target instanceof Node)) return;
94
+ if (!surface?.contains(event.target) && !anchor.contains(event.target)) return;
95
+ event.preventDefault();
96
+ dismiss(true);
97
+ };
98
+ const observer = typeof ResizeObserver === "undefined" ? undefined : new ResizeObserver(position);
99
+ observer?.observe(anchor);
100
+ const visibility = typeof IntersectionObserver === "undefined" ? undefined : new IntersectionObserver(entries => {
101
+ inView = entries.at(-1)?.isIntersecting ?? inView;
102
+ position();
103
+ });
104
+ visibility?.observe(anchor);
105
+ surface.addEventListener("focusin", focusIn);
106
+ document.addEventListener("keydown", keydown);
107
+ window.addEventListener("resize", position);
108
+ window.addEventListener("scroll", position, {
109
+ capture: true,
110
+ passive: true
111
+ });
112
+ position();
113
+ onCleanup(() => {
114
+ observer?.disconnect();
115
+ visibility?.disconnect();
116
+ surface?.removeEventListener("focusin", focusIn);
117
+ document.removeEventListener("keydown", keydown);
118
+ window.removeEventListener("resize", position);
119
+ window.removeEventListener("scroll", position, true);
120
+ hide();
121
+ });
122
+ });
123
+ return _$createComponent(Show, {
124
+ get when() {
125
+ return props.open;
126
+ },
127
+ get children() {
128
+ var _el$ = _$getNextElement(_tmpl$),
129
+ _el$2 = _el$.firstChild,
130
+ _el$3 = _el$2.nextSibling,
131
+ _el$4 = _el$3.firstChild,
132
+ [_el$5, _co$] = _$getNextMarker(_el$4.nextSibling),
133
+ _el$6 = _el$5.nextSibling,
134
+ [_el$7, _co$2] = _$getNextMarker(_el$6.nextSibling);
135
+ var _ref$ = surface;
136
+ typeof _ref$ === "function" ? _$use(_ref$, _el$) : surface = _el$;
137
+ _$setAttribute(_el$, "aria-labelledby", textId);
138
+ var _ref$2 = text;
139
+ typeof _ref$2 === "function" ? _$use(_ref$2, _el$2) : text = _el$2;
140
+ _$setAttribute(_el$2, "id", textId);
141
+ _$insert(_el$2, () => props.children);
142
+ _$insert(_el$3, _$createComponent(Show, {
143
+ get when() {
144
+ return props.action.href !== undefined;
145
+ },
146
+ get fallback() {
147
+ return _$createComponent(Button, {
148
+ size: "sm",
149
+ variant: "secondary",
150
+ type: "button",
151
+ onClick: () => {
152
+ const action = props.action;
153
+ if (dismiss(false)) action.onClick?.();
154
+ },
155
+ get children() {
156
+ return props.action.label;
157
+ }
158
+ });
159
+ },
160
+ get children() {
161
+ return _$createComponent(ButtonLink, {
162
+ size: "sm",
163
+ variant: "secondary",
164
+ get href() {
165
+ return props.action.href;
166
+ },
167
+ onClick: () => dismiss(false),
168
+ get children() {
169
+ return props.action.label;
170
+ }
171
+ });
172
+ }
173
+ }), _el$5, _co$);
174
+ _$insert(_el$3, _$createComponent(Button, {
175
+ size: "sm",
176
+ variant: "ghost",
177
+ type: "button",
178
+ onClick: () => dismiss(true),
179
+ get children() {
180
+ return messages().movedHintDismiss;
181
+ }
182
+ }), _el$7, _co$2);
183
+ _$effect(_p$ => {
184
+ var _v$ = `k2b-moved-hint${props.class ? ` ${props.class}` : ""}`,
185
+ _v$2 = props.placement ?? "bottom";
186
+ _v$ !== _p$.e && _$className(_el$, _p$.e = _v$);
187
+ _v$2 !== _p$.t && _$setAttribute(_el$, "data-placement", _p$.t = _v$2);
188
+ return _p$;
189
+ }, {
190
+ e: undefined,
191
+ t: undefined
192
+ });
193
+ return _el$;
194
+ }
195
+ });
196
+ }
197
+ //# sourceMappingURL=MovedHint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["feedback/MovedHint.tsx"],"sourcesContent":["import { createEffect, createSignal, createUniqueId, type JSX, onCleanup, onMount, Show } from \"solid-js\";\nimport { Button, ButtonLink } from \"../actions/Button\";\nimport { useUiMessages } from \"../intl/messages\";\nimport { announce } from \"./announce\";\nimport { positionTooltipSurface, type TooltipPlacement } from \"./tooltip-position\";\n\nexport type MovedHintAction = { label: string; href: string; onClick?: never } | { label: string; onClick: () => void; href?: never };\n\nexport type MovedHintProps = {\n /** The owner decides this on the server, so a dismissed hint never renders again. */\n open: boolean;\n /** Called once per open period when the person closes the hint or uses its action. */\n onDismiss: () => void;\n /** Render the hint directly after this element to preserve keyboard order. */\n target: () => HTMLElement | undefined;\n /** Short text naming the new place. */\n children: JSX.Element;\n action: MovedHintAction;\n placement?: TooltipPlacement;\n class?: string;\n};\n\nexport function MovedHint(props: MovedHintProps): JSX.Element {\n const messages = useUiMessages();\n const textId = `k2b-moved-hint-${createUniqueId()}`;\n const [mounted, setMounted] = createSignal(false);\n const [dismissed, setDismissed] = createSignal(false);\n let surface: HTMLDivElement | undefined;\n let text: HTMLParagraphElement | undefined;\n let previousFocus: HTMLElement | undefined;\n let shown = false;\n let announced = false;\n\n const hide = () => {\n if (!shown) return;\n shown = false;\n try {\n surface?.hidePopover?.();\n } catch {\n // Cleanup can run after the surface disconnects.\n }\n };\n\n const dismiss = (restoreFocus: boolean): boolean => {\n if (!props.open || dismissed()) return false;\n const focusInside = surface?.contains(document.activeElement);\n const target = props.target();\n const returnTo =\n previousFocus?.isConnected && !previousFocus.matches(\":disabled\")\n ? previousFocus\n : target && (target.tabIndex >= 0 || target.hasAttribute(\"tabindex\")) && !target.matches(\":disabled\")\n ? target\n : undefined;\n hide();\n if (restoreFocus && focusInside && returnTo) {\n returnTo.focus({ preventScroll: true });\n }\n setDismissed(true);\n props.onDismiss();\n return true;\n };\n\n createEffect(() => {\n if (!props.open) {\n setDismissed(false);\n previousFocus = undefined;\n }\n });\n onMount(() => setMounted(true));\n\n createEffect(() => {\n if (!mounted() || !props.open || dismissed()) return;\n const anchor = props.target();\n const placement = props.placement ?? \"bottom\";\n if (!surface || !anchor) return;\n\n // A target scrolled out of view, or clipped by its scroller, takes the hint along instead of leaving it\n // pinned to the viewport edge over unrelated content.\n let inView = typeof IntersectionObserver === \"undefined\";\n const position = () => {\n if (!props.open || dismissed() || !inView || !anchor.isConnected || anchor.getClientRects().length === 0) {\n hide();\n return;\n }\n if (!surface || typeof surface.showPopover !== \"function\") return;\n if (!shown) {\n try {\n surface.showPopover();\n shown = true;\n } catch {\n return;\n }\n }\n positionTooltipSurface(surface, anchor, placement);\n if (!announced) {\n announced = true;\n announce(text?.textContent ?? \"\");\n }\n };\n const focusIn = (event: FocusEvent) => {\n if (!(event.relatedTarget instanceof Node) || !surface?.contains(event.relatedTarget)) {\n previousFocus = event.relatedTarget instanceof HTMLElement ? event.relatedTarget : undefined;\n }\n };\n const keydown = (event: KeyboardEvent) => {\n if (event.defaultPrevented || event.isComposing) return;\n if (event.key !== \"Escape\" || !(event.target instanceof Node)) return;\n if (!surface?.contains(event.target) && !anchor.contains(event.target)) return;\n event.preventDefault();\n dismiss(true);\n };\n const observer = typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(position);\n observer?.observe(anchor);\n const visibility =\n typeof IntersectionObserver === \"undefined\"\n ? undefined\n : new IntersectionObserver((entries) => {\n inView = entries.at(-1)?.isIntersecting ?? inView;\n position();\n });\n visibility?.observe(anchor);\n surface.addEventListener(\"focusin\", focusIn);\n document.addEventListener(\"keydown\", keydown);\n window.addEventListener(\"resize\", position);\n window.addEventListener(\"scroll\", position, { capture: true, passive: true });\n position();\n onCleanup(() => {\n observer?.disconnect();\n visibility?.disconnect();\n surface?.removeEventListener(\"focusin\", focusIn);\n document.removeEventListener(\"keydown\", keydown);\n window.removeEventListener(\"resize\", position);\n window.removeEventListener(\"scroll\", position, true);\n hide();\n });\n });\n\n return (\n <Show when={props.open}>\n <div\n ref={surface}\n class={`k2b-moved-hint${props.class ? ` ${props.class}` : \"\"}`}\n popover=\"manual\"\n role=\"group\"\n aria-labelledby={textId}\n data-placement={props.placement ?? \"bottom\"}\n >\n <p ref={text} class=\"k2b-moved-hint__text\" id={textId}>\n {props.children}\n </p>\n <div class=\"k2b-moved-hint__actions\">\n <Show\n when={props.action.href !== undefined}\n fallback={\n <Button\n size=\"sm\"\n variant=\"secondary\"\n type=\"button\"\n onClick={() => {\n const action = props.action;\n if (dismiss(false)) action.onClick?.();\n }}\n >\n {props.action.label}\n </Button>\n }\n >\n <ButtonLink size=\"sm\" variant=\"secondary\" href={props.action.href} onClick={() => dismiss(false)}>\n {props.action.label}\n </ButtonLink>\n </Show>\n <Button size=\"sm\" variant=\"ghost\" type=\"button\" onClick={() => dismiss(true)}>\n {messages().movedHintDismiss}\n </Button>\n </div>\n </div>\n </Show>\n );\n}\n"],"mappings":"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","ignoreList":[]}
@@ -58,6 +58,9 @@ const focusUnclaimed = dialog => {
58
58
  const active = document.activeElement;
59
59
  return !active || active === document.body || active === dialog;
60
60
  };
61
+
62
+ /** Checked by shape: a test harness or an iframe may create the event in another realm. */
63
+ const isPopState = value => typeof value === "object" && value !== null && "type" in value && value.type === "popstate" && "state" in value;
61
64
  export const createDialogCore = () => {
62
65
  const state = {
63
66
  stack: []
@@ -65,17 +68,26 @@ export const createDialogCore = () => {
65
68
  /** Markers of this core's entries in the browser history, oldest first. */
66
69
  let historyIds = [];
67
70
  const issued = new Set();
71
+ /**
72
+ * Markers forgotten during island disposal. Their entries stay in `historyIds` as barriers: a synthetic Back never
73
+ * crosses one, because the router may have replaced that entry with another page.
74
+ */
75
+ const forgotten = new Set();
76
+ const deadMarker = marker => typeof marker === "number" && forgotten.has(marker);
77
+ /** How often islands were disposed, so a dialog that waits to open notices that a content swap happened meanwhile. */
78
+ let disposals = 0;
68
79
  const ownMarker = marker => typeof marker === "number" && issued.has(marker);
69
80
  let seen;
70
81
  let traversalFrom;
71
82
  let syntheticFrom;
72
83
  const historyHolders = new Set();
73
84
  let historySyncScheduled = false;
85
+ let disposingIslands = false;
74
86
  /** Settles when the synthetic Back that removes closed dialogs' entries has arrived. */
75
87
  let returning;
76
88
  let resolveReturning;
77
89
 
78
- /** Closed entries leave with one Back to the newest live dialog or holder, or to the page. */
90
+ /** Outside island disposal, closed entries leave with one Back to the newest live dialog or holder, or to the page. */
79
91
  const syncHistory = () => {
80
92
  if (returning) {
81
93
  void returning.then(() => {
@@ -87,7 +99,7 @@ export const createDialogCore = () => {
87
99
  const openIds = new Set([...state.stack, ...historyHolders].map(entry => entry.historyId));
88
100
  const current = historyIds.at(-1);
89
101
  let steps = 0;
90
- while (historyIds.length > 0 && !openIds.has(historyIds.at(-1))) {
102
+ while (historyIds.length > 0 && !openIds.has(historyIds.at(-1)) && !deadMarker(historyIds.at(-1))) {
91
103
  historyIds.pop();
92
104
  steps++;
93
105
  }
@@ -107,13 +119,26 @@ export const createDialogCore = () => {
107
119
  history.go(-steps);
108
120
  };
109
121
  const releaseHistory = entry => {
110
- if (entry.historyId === undefined || historySyncScheduled) return;
122
+ if (entry.historyId === undefined) return;
123
+ // During a content swap the router owns history. Forget the marker without moving history; its entry stays a
124
+ // barrier for later synthetic Backs and is left for the router to replace.
125
+ if (disposingIslands) {
126
+ issued.delete(entry.historyId);
127
+ forgotten.add(entry.historyId);
128
+ entry.historyId = undefined;
129
+ return;
130
+ }
131
+ if (historySyncScheduled) return;
111
132
  historySyncScheduled = true;
112
133
  queueMicrotask(syncHistory);
113
134
  };
114
135
 
136
+ /** Traversals this core already handled: a page swap router also reports the ones it hides from listeners. */
137
+ const handled = new WeakSet();
115
138
  /** Back or Forward moved to another entry: close dialogs and holders opened after the one it shows. */
116
139
  const onPopState = event => {
140
+ if (handled.has(event)) return;
141
+ handled.add(event);
117
142
  const resolveBack = resolveReturning;
118
143
  // Without the Navigation API, only our synthetic Back has a known departure.
119
144
  const from = typeof navigation !== "undefined" ? traversalFrom : resolveBack ? syntheticFrom : undefined;
@@ -126,8 +151,8 @@ export const createDialogCore = () => {
126
151
  event.stopImmediatePropagation();
127
152
  }
128
153
  // Own entries are ordered by id. A retired entry reached by Forward stays tracked, so closing the dialogs
129
- // beneath it also removes it.
130
- historyIds = ownMarker(marker) ? [...historyIds.filter(id => id < marker), marker] : [];
154
+ // beneath it also removes it. A forgotten entry keeps the holders opened before it and stays a barrier.
155
+ historyIds = ownMarker(marker) || deadMarker(marker) ? [...historyIds.filter(id => id < marker), marker] : [];
131
156
  const gone = entry => entry.historyId !== undefined && !historyIds.includes(entry.historyId);
132
157
  while (state.stack.some(gone)) popTop(undefined);
133
158
  for (const holder of [...historyHolders].reverse()) {
@@ -141,6 +166,36 @@ export const createDialogCore = () => {
141
166
  resolveBack?.();
142
167
  }
143
168
  };
169
+
170
+ /**
171
+ * Island disposal means the page content changes, as a document load would. Every dialog closes, top first, whichever
172
+ * code opened it: which island calls open() is not knowable after an await, in onMount or from a timer, and a modal
173
+ * left over the new content would block it.
174
+ */
175
+ const onIslandDispose = () => {
176
+ if (!disposingIslands) {
177
+ disposingIslands = true;
178
+ queueMicrotask(() => {
179
+ disposingIslands = false;
180
+ });
181
+ // Entries released just before, whose Back has not started yet, stay as well: the router owns history now.
182
+ // A Back already on its way still settles through `returning`.
183
+ historySyncScheduled = false;
184
+ const live = new Set([...state.stack, ...historyHolders].map(entry => entry.historyId));
185
+ for (const id of historyIds) {
186
+ if (live.has(id) || !issued.delete(id)) continue;
187
+ forgotten.add(id);
188
+ }
189
+ }
190
+ disposals++;
191
+ while (state.stack.length > 0) popTop(undefined);
192
+ };
193
+
194
+ /** `@k2b/ssr/nav` stops a traversal to another server page before later listeners and reports it here. */
195
+ const onHiddenPopState = event => {
196
+ const detail = "detail" in event ? event.detail : undefined;
197
+ if (isPopState(detail)) onPopState(detail);
198
+ };
144
199
  const onNavigate = event => {
145
200
  if (event.navigationType === "traverse") traversalFrom = {
146
201
  href: location.href,
@@ -154,10 +209,23 @@ export const createDialogCore = () => {
154
209
  listening?.window.removeEventListener("popstate", onPopState, {
155
210
  capture: true
156
211
  });
212
+ listening?.window.removeEventListener("ssr:nav-popstate", onHiddenPopState, {
213
+ capture: true
214
+ });
215
+ listening?.window.removeEventListener("ssr:island-dispose", onIslandDispose, {
216
+ capture: true
217
+ });
157
218
  listening?.navigation?.removeEventListener("navigate", onNavigate);
158
219
  window.addEventListener("popstate", onPopState, {
159
220
  capture: true
160
221
  });
222
+ window.addEventListener("ssr:nav-popstate", onHiddenPopState, {
223
+ capture: true
224
+ });
225
+ // Capture opens the disposal window before target listeners can release history holders.
226
+ window.addEventListener("ssr:island-dispose", onIslandDispose, {
227
+ capture: true
228
+ });
161
229
  const current = typeof navigation !== "undefined" ? navigation : undefined;
162
230
  current?.addEventListener("navigate", onNavigate);
163
231
  listening = {
@@ -206,7 +274,7 @@ export const createDialogCore = () => {
206
274
  };
207
275
 
208
276
  /**
209
- * Settles once released entries have left the history. A caller that navigates or pushes an entry of its own waits
277
+ * Settles once released entries have left history or were forgotten during island disposal. A caller that navigates waits
210
278
  * for it, as a dialog's result waits: a Back still on its way would otherwise land behind the new entry.
211
279
  */
212
280
  const historySettled = async () => {
@@ -386,13 +454,19 @@ export const createDialogCore = () => {
386
454
  });
387
455
  };
388
456
  const open = (view, options = {}) => {
457
+ return openAfter(view, options, disposals);
458
+ };
459
+ const openAfter = (view, options, requested) => {
389
460
  if (options.signal?.aborted) return Promise.resolve(undefined);
390
461
  if (options.history) {
391
462
  // Dialogs closed just now leave the history first, or this dialog's entry would be the one removed.
392
463
  if (historySyncScheduled) syncHistory();
393
- if (returning) return returning.then(() => open(view, options));
464
+ if (returning) return returning.then(() => openAfter(view, options, requested));
394
465
  }
466
+ // A dialog that waited for history while islands were disposed would open over content it was never meant for.
467
+ if (disposals !== requested) return Promise.resolve(undefined);
395
468
  const dialog = ensureDialogElement();
469
+ listen();
396
470
  const previousTop = state.stack[state.stack.length - 1];
397
471
  if (previousTop) previousTop.container.style.display = "none";
398
472
  const activeElement = document.activeElement;
@@ -427,11 +501,12 @@ export const createDialogCore = () => {
427
501
  };
428
502
  entry.resolve = value => {
429
503
  options.signal?.removeEventListener("abort", abort);
430
- if (entry.historyId === undefined) {
504
+ if (!options.history) {
431
505
  resolve(value);
432
506
  return;
433
507
  }
434
508
  // After every pending history sync, so a caller that navigates or opens the next dialog finds the history settled.
509
+ // This holds for a marker forgotten during island disposal too, while an earlier synthetic Back is on its way.
435
510
  queueMicrotask(() => void historySettled().then(() => resolve(value)));
436
511
  };
437
512
  const closeTyped = result => {
@@ -522,8 +597,9 @@ export const dialogCore = publicDialogCore;
522
597
  /**
523
598
  * Internal: transient UI shares dialogs' Back handling without exposing another public dialog capability.
524
599
  * Same-URL traversals are suppressed with the Navigation API; without it, only the synthetic Back on release is suppressed.
600
+ * Release during island disposal forgets the marker without traversing; the router may replace the remaining entry.
525
601
  */
526
602
  export const holdDialogHistory = holdHistory;
527
- /** Internal: waits until entries released through `holdDialogHistory` have left the history. */
603
+ /** Internal: waits until released holders have left history or were forgotten during island disposal. */
528
604
  export const dialogHistorySettled = historySettled;
529
605
  //# sourceMappingURL=dialog-core.js.map