@k2b/ui 0.45.0 → 0.47.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 (121) hide show
  1. package/dist/browser/actions/Dropdown.js +8 -1
  2. package/dist/browser/actions/Dropdown.js.map +1 -1
  3. package/dist/browser/actions/FilterChip.js +11 -5
  4. package/dist/browser/actions/FilterChip.js.map +1 -1
  5. package/dist/browser/actions/ViewTabs.js +225 -93
  6. package/dist/browser/actions/ViewTabs.js.map +1 -1
  7. package/dist/browser/chat/ChatDecision.js +10 -6
  8. package/dist/browser/chat/ChatDecision.js.map +1 -1
  9. package/dist/browser/content/FileGrid.js +125 -99
  10. package/dist/browser/content/FileGrid.js.map +1 -1
  11. package/dist/browser/content/VirtualFeed.js +26 -21
  12. package/dist/browser/content/VirtualFeed.js.map +1 -1
  13. package/dist/browser/inputs/Combobox.js +14 -6
  14. package/dist/browser/inputs/Combobox.js.map +1 -1
  15. package/dist/browser/inputs/choice.js +36 -9
  16. package/dist/browser/inputs/choice.js.map +1 -1
  17. package/dist/browser/layout/AnchoredPopover.js +2 -1
  18. package/dist/browser/layout/AnchoredPopover.js.map +1 -1
  19. package/dist/browser/layout/AppWorkspace.js +348 -444
  20. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  21. package/dist/browser/layout/AppWorkspaceSidebarItemStatus.js +113 -0
  22. package/dist/browser/layout/AppWorkspaceSidebarItemStatus.js.map +1 -0
  23. package/dist/browser/layout/DetailPanel.js +130 -108
  24. package/dist/browser/layout/DetailPanel.js.map +1 -1
  25. package/dist/browser/layout/DetailSections.js +1123 -0
  26. package/dist/browser/layout/DetailSections.js.map +1 -0
  27. package/dist/browser/layout/DetailsToggle.js +32 -0
  28. package/dist/browser/layout/DetailsToggle.js.map +1 -0
  29. package/dist/browser/layout/Discussion.js +3 -0
  30. package/dist/browser/layout/Discussion.js.map +1 -1
  31. package/dist/browser/layout/MobileShell.js +141 -102
  32. package/dist/browser/layout/MobileShell.js.map +1 -1
  33. package/dist/browser/layout/PageTitle.js +33 -10
  34. package/dist/browser/layout/PageTitle.js.map +1 -1
  35. package/dist/browser/layout/app-workspace-controller.js +12 -0
  36. package/dist/browser/layout/app-workspace-controller.js.map +1 -1
  37. package/dist/browser/layout/detail-sections-messages.js +127 -0
  38. package/dist/browser/layout/detail-sections-messages.js.map +1 -0
  39. package/dist/browser/layout/index.js +3 -0
  40. package/dist/browser/layout/index.js.map +1 -1
  41. package/dist/browser/surfaces/Avatar.js +3 -1
  42. package/dist/browser/surfaces/Avatar.js.map +1 -1
  43. package/dist/browser/surfaces/AvatarStack.js +95 -61
  44. package/dist/browser/surfaces/AvatarStack.js.map +1 -1
  45. package/dist/browser/surfaces/ChangeList.js +77 -0
  46. package/dist/browser/surfaces/ChangeList.js.map +1 -0
  47. package/dist/browser/surfaces/index.js +2 -1
  48. package/dist/browser/surfaces/index.js.map +1 -1
  49. package/dist/browser/text-editor/TextEditor.js +19 -8
  50. package/dist/browser/text-editor/TextEditor.js.map +1 -1
  51. package/dist/ssr/actions/Dropdown.js +6 -0
  52. package/dist/ssr/actions/Dropdown.js.map +1 -1
  53. package/dist/ssr/actions/FilterChip.js +1 -1
  54. package/dist/ssr/actions/FilterChip.js.map +1 -1
  55. package/dist/ssr/actions/ViewTabs.js +144 -37
  56. package/dist/ssr/actions/ViewTabs.js.map +1 -1
  57. package/dist/ssr/chat/ChatDecision.js +10 -6
  58. package/dist/ssr/chat/ChatDecision.js.map +1 -1
  59. package/dist/ssr/content/FileGrid.js +36 -22
  60. package/dist/ssr/content/FileGrid.js.map +1 -1
  61. package/dist/ssr/content/VirtualFeed.js +26 -21
  62. package/dist/ssr/content/VirtualFeed.js.map +1 -1
  63. package/dist/ssr/inputs/Combobox.js +6 -3
  64. package/dist/ssr/inputs/Combobox.js.map +1 -1
  65. package/dist/ssr/inputs/choice.js +36 -9
  66. package/dist/ssr/inputs/choice.js.map +1 -1
  67. package/dist/ssr/layout/AnchoredPopover.js +2 -1
  68. package/dist/ssr/layout/AnchoredPopover.js.map +1 -1
  69. package/dist/ssr/layout/AppWorkspace.js +66 -132
  70. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  71. package/dist/ssr/layout/AppWorkspaceSidebarItemStatus.js +76 -0
  72. package/dist/ssr/layout/AppWorkspaceSidebarItemStatus.js.map +1 -0
  73. package/dist/ssr/layout/DetailPanel.js +18 -1
  74. package/dist/ssr/layout/DetailPanel.js.map +1 -1
  75. package/dist/ssr/layout/DetailSections.js +838 -0
  76. package/dist/ssr/layout/DetailSections.js.map +1 -0
  77. package/dist/ssr/layout/DetailsToggle.js +29 -0
  78. package/dist/ssr/layout/DetailsToggle.js.map +1 -0
  79. package/dist/ssr/layout/Discussion.js +3 -0
  80. package/dist/ssr/layout/Discussion.js.map +1 -1
  81. package/dist/ssr/layout/MobileShell.js +60 -29
  82. package/dist/ssr/layout/MobileShell.js.map +1 -1
  83. package/dist/ssr/layout/PageTitle.js +29 -6
  84. package/dist/ssr/layout/PageTitle.js.map +1 -1
  85. package/dist/ssr/layout/app-workspace-controller.js +12 -0
  86. package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
  87. package/dist/ssr/layout/detail-sections-messages.js +127 -0
  88. package/dist/ssr/layout/detail-sections-messages.js.map +1 -0
  89. package/dist/ssr/layout/index.js +3 -0
  90. package/dist/ssr/layout/index.js.map +1 -1
  91. package/dist/ssr/surfaces/Avatar.js +3 -1
  92. package/dist/ssr/surfaces/Avatar.js.map +1 -1
  93. package/dist/ssr/surfaces/AvatarStack.js +61 -32
  94. package/dist/ssr/surfaces/AvatarStack.js.map +1 -1
  95. package/dist/ssr/surfaces/ChangeList.js +37 -0
  96. package/dist/ssr/surfaces/ChangeList.js.map +1 -0
  97. package/dist/ssr/surfaces/index.js +2 -1
  98. package/dist/ssr/surfaces/index.js.map +1 -1
  99. package/dist/ssr/text-editor/TextEditor.js +19 -8
  100. package/dist/ssr/text-editor/TextEditor.js.map +1 -1
  101. package/dist/styles.css +1 -1
  102. package/dist/types/actions/FilterChip.d.ts +4 -0
  103. package/dist/types/actions/ViewTabs.d.ts +11 -1
  104. package/dist/types/chat/ChatDecision.d.ts +5 -0
  105. package/dist/types/content/FileGrid.d.ts +6 -0
  106. package/dist/types/inputs/Combobox.d.ts +2 -0
  107. package/dist/types/inputs/choice.d.ts +9 -3
  108. package/dist/types/layout/AppWorkspace.d.ts +2 -28
  109. package/dist/types/layout/AppWorkspaceSidebarItemStatus.d.ts +30 -0
  110. package/dist/types/layout/DetailPanel.d.ts +9 -0
  111. package/dist/types/layout/DetailSections.d.ts +186 -0
  112. package/dist/types/layout/DetailsToggle.d.ts +13 -0
  113. package/dist/types/layout/MobileShell.d.ts +12 -0
  114. package/dist/types/layout/PageTitle.d.ts +7 -0
  115. package/dist/types/layout/detail-sections-messages.d.ts +61 -0
  116. package/dist/types/layout/index.d.ts +5 -1
  117. package/dist/types/surfaces/Avatar.d.ts +3 -0
  118. package/dist/types/surfaces/AvatarStack.d.ts +20 -1
  119. package/dist/types/surfaces/ChangeList.d.ts +20 -0
  120. package/dist/types/surfaces/index.d.ts +5 -3
  121. package/package.json +3 -3
@@ -0,0 +1,838 @@
1
+ import { mergeProps as _$mergeProps } from "solid-js/web";
2
+ import { ssrStyleProperty as _$ssrStyleProperty } from "solid-js/web";
3
+ import { memo as _$memo } from "solid-js/web";
4
+ import { ssr as _$ssr } from "solid-js/web";
5
+ import { createComponent as _$createComponent } from "solid-js/web";
6
+ import { ssrAttribute as _$ssrAttribute } from "solid-js/web";
7
+ import { escape as _$escape } from "solid-js/web";
8
+ var _tmpl$ = ["<span class=\"k2b-detail-presence__face\"", ">", "<span class=\"k2b-detail-presence__dot\" aria-hidden=\"true\"></span></span>"],
9
+ _tmpl$2 = ["<span class=\"k2b-detail-presence__swatch\" style=\"", "\" aria-hidden=\"true\"></span>"],
10
+ _tmpl$3 = ["<strong>", "</strong>"],
11
+ _tmpl$4 = ["<i", " aria-hidden=\"true\"></i>"],
12
+ _tmpl$5 = ["<button type=\"button\" class=\"k2b-detail-presence__chip\" style=\"", "\"", "><span aria-hidden=\"true\" class=\"k2b-detail-presence__chip-content\">", "</span></button>"],
13
+ _tmpl$6 = ["<span class=\"k2b-detail-presence__chip\" style=\"", "\"", "><span class=\"k2b-sr-only\">", "</span><span aria-hidden=\"true\" class=\"k2b-detail-presence__chip-content\">", "</span></span>"],
14
+ _tmpl$7 = ["<div class=\"k2b-detail-presence__strip\"><div class=\"k2b-detail-presence__chips\">", "</div>", "</div>"],
15
+ _tmpl$8 = ["<p class=\"k2b-detail-presence__where k2b-truncate-fade\">", "</p>"],
16
+ _tmpl$9 = ["<div class=\"k2b-detail-presence\"", ">", "</div>"],
17
+ _tmpl$0 = ["<div class=\"k2b-detail-presence__strip k2b-detail-presence__problem\" role=\"status\"><i class=\"ti ti-alert-triangle\" aria-hidden=\"true\"></i><span class=\"k2b-truncate-fade\">", "</span>", "</div>"],
18
+ _tmpl$1 = "<div class=\"k2b-detail-presence__strip\" aria-hidden=\"true\"><span class=\"k2b-detail-presence__skeleton\"></span><span class=\"k2b-detail-presence__skeleton\"></span><span class=\"k2b-detail-presence__skeleton\"></span></div>",
19
+ _tmpl$10 = ["<p class=\"k2b-detail-presence__where\"><span class=\"k2b-detail-presence__skeleton-line\" aria-hidden=\"true\"></span><span class=\"k2b-sr-only\" role=\"status\">", "</span></p>"],
20
+ _tmpl$11 = "<i class=\"ti ti-refresh\" aria-hidden=\"true\"></i>",
21
+ _tmpl$12 = ["<span class=\"k2b-detail-presence__more\"", ">+", "</span>"],
22
+ _tmpl$13 = ["<span class=\"k2b-detail-presence__self\">", "", "</span>"],
23
+ _tmpl$14 = ["<span class=\"k2b-detail-presence-slot__more\">+", "</span>"],
24
+ _tmpl$15 = ["<span class=\"k2b-detail-presence-slot\" aria-hidden=\"true\">", "", "</span>"],
25
+ _tmpl$16 = ["<span class=\"k2b-sr-only\" role=\"status\">", "</span>"],
26
+ _tmpl$17 = ["<div class=\"k2b-detail-section-error\" role=\"alert\"><i class=\"ti ti-alert-circle\" aria-hidden=\"true\"></i><span>", "</span>", "</div>"],
27
+ _tmpl$18 = ["<p class=\"k2b-detail-section-note\">", "</p>"],
28
+ _tmpl$19 = ["<div class=\"k2b-detail-section-rows\"", ">", "", "", "", "", "</div>"],
29
+ _tmpl$20 = "<span class=\"k2b-detail-row-skeleton__face\"></span>",
30
+ _tmpl$21 = "<span class=\"k2b-detail-row-skeleton\"></span>",
31
+ _tmpl$22 = "<i class=\"ti ti-settings\" aria-hidden=\"true\"></i>",
32
+ _tmpl$23 = ["<div class=\"k2b-detail-section-group\"><div class=\"k2b-detail-section-label\">", "</div>", "", "</div>"],
33
+ _tmpl$24 = ["<div class=\"k2b-detail-section-rows\" aria-busy=\"true\">", "<span class=\"k2b-sr-only\" role=\"status\">", "</span></div>"],
34
+ _tmpl$25 = ["<div class=\"k2b-detail-section-rows\">", "", "", "", "</div>"],
35
+ _tmpl$26 = ["<div class=\"k2b-detail-section-rows\">", "", "</div>"],
36
+ _tmpl$27 = ["<dl class=\"k2b-detail-properties\">", "</dl>"],
37
+ _tmpl$28 = ["<span class=\"k2b-detail-properties__leading\">", "</span>"],
38
+ _tmpl$29 = ["<div class=\"k2b-detail-properties__item\"><dt>", "</dt><dd>", "", "</dd></div>"],
39
+ _tmpl$30 = ["<span class=\"k2b-detail-properties__value\">", "</span>"],
40
+ _tmpl$31 = ["<a class=\"k2b-detail-properties__value k2b-detail-properties__link\"", ">", "</a>"],
41
+ _tmpl$32 = ["<ul class=\"k2b-detail-tags\">", "</ul>"],
42
+ _tmpl$33 = ["<li>", "</li>"],
43
+ _tmpl$34 = ["<span class=\"k2b-detail-tag\">#", "</span>"],
44
+ _tmpl$35 = ["<a class=\"k2b-detail-tag\"", ">#", "</a>"];
45
+ import { children, createMemo, createSignal, For, onCleanup, Show } from "solid-js";
46
+ import { Button } from "../actions/Button";
47
+ import { Avatar } from "../surfaces/Avatar";
48
+ import { AvatarStack } from "../surfaces/AvatarStack";
49
+ import { personColor } from "../surfaces/person-color";
50
+ import DetailPanel from "./DetailPanel";
51
+ import { useDetailPanelMessages } from "./detail-panel-messages";
52
+ import { useDetailSectionMessages } from "./detail-sections-messages";
53
+
54
+ /*
55
+ * Shared sections of an object's detail panel. Each one is a `DetailPanel.Section` with its icon, title, and count, so
56
+ * its content starts on the title's text edge, and each keeps its height while its data changes. Apps pass data; the
57
+ * order of sections is a convention (see the DetailPanel UI context).
58
+ */
59
+
60
+ const listNames = (names, and) => names.length <= 1 ? names[0] ?? "" : `${names.slice(0, -1).join(", ")} ${and} ${names.at(-1)}`;
61
+
62
+ // ── Presence ────────────────────────────────────────────────────────────
63
+
64
+ /** `offline` keeps the last known people, pale and without live dots; `unavailable` offers `onRetry`. */
65
+
66
+ const PresenceFace = props => _$ssr(_tmpl$, _$ssrAttribute("data-live", props.live ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Avatar, {
67
+ get name() {
68
+ return props.person.name;
69
+ },
70
+ get src() {
71
+ return props.person.src;
72
+ },
73
+ get icon() {
74
+ return props.person.icon;
75
+ },
76
+ get shape() {
77
+ return props.person.shape;
78
+ },
79
+ size: "xs",
80
+ alt: ""
81
+ })));
82
+
83
+ /**
84
+ * "Here now": who has the object open, what they do, and where. Always two lines tall in every state, so people
85
+ * coming, going, or moving never shift the panel. Chips that do not fit wrap into a hidden line and count as "+n";
86
+ * "+n" and "You" open the full list, which `AvatarStack` shows beside it or as a sheet on phones.
87
+ */
88
+ export function DetailPresence(props) {
89
+ const t = useDetailSectionMessages();
90
+ const state = () => props.state ?? "live";
91
+ const others = createMemo(() => props.people.filter(person => !person.self));
92
+ const self = createMemo(() => props.people.find(person => person.self));
93
+ const short = person => person.shortName ?? person.name;
94
+ const color = person => person.color ?? personColor(person.name);
95
+ const [hidden, setHidden] = createSignal(0);
96
+ let chips;
97
+
98
+ // Chips that do not fit wrap into the clipped second line; they leave the tab order and count as "+n".
99
+ const fit = () => {
100
+ if (!chips) return;
101
+ const all = Array.from(chips.children).filter(child => child instanceof HTMLElement);
102
+ const top = all[0]?.offsetTop ?? 0;
103
+ let wrapped = 0;
104
+ for (const chip of all) {
105
+ const out = chip.offsetTop > top;
106
+ chip.inert = out;
107
+ if (out) wrapped += 1;
108
+ }
109
+ setHidden(wrapped);
110
+ };
111
+ // The strip mounts again after loading or an outage, so each mounted strip watches itself.
112
+ const watchChips = element => {
113
+ chips = element;
114
+ if (typeof ResizeObserver === "undefined") return;
115
+ const resized = new ResizeObserver(fit);
116
+ resized.observe(element);
117
+ const changed = new MutationObserver(fit);
118
+ changed.observe(element, {
119
+ childList: true
120
+ });
121
+ queueMicrotask(fit);
122
+ onCleanup(() => {
123
+ resized.disconnect();
124
+ changed.disconnect();
125
+ });
126
+ };
127
+ const describe = person => person.activity === "writing" ? t().personWriting({
128
+ name: person.name,
129
+ place: person.place ?? ""
130
+ }) : t().personReading({
131
+ name: person.name
132
+ });
133
+ const listPeople = createMemo(() => props.people.map(person => ({
134
+ id: person.id,
135
+ name: person.self ? t().you : person.name,
136
+ src: person.src,
137
+ icon: person.icon,
138
+ shape: person.shape,
139
+ self: person.self,
140
+ detailIcon: person.self ? undefined : person.activity === "writing" ? "ti ti-pencil" : "ti ti-eye",
141
+ detail: [person.self ? undefined : person.activity === "writing" ? t().writing : t().reading, person.place].filter(Boolean).join(" · "),
142
+ trailing: person.trailing,
143
+ onSelect: person.onSelect
144
+ })));
145
+ const where = () => {
146
+ if (state() === "offline" || others().length === 0) return props.note;
147
+ const writers = others().filter(person => person.activity === "writing");
148
+ const readers = others().filter(person => person.activity !== "writing");
149
+ return [_$createComponent(For, {
150
+ each: writers,
151
+ children: (person, index) => [index() > 0 ? ", " : "", _$ssr(_tmpl$2, _$ssrStyleProperty("--k2b-person-color:", _$escape(color(person), true))), _$ssr(_tmpl$3, _$escape(short(person))), _$createComponent(Show, {
152
+ get when() {
153
+ return person.place;
154
+ },
155
+ children: place => [" ", t().writesIn, " ", place()]
156
+ })]
157
+ }), _$createComponent(Show, {
158
+ get when() {
159
+ return _$memo(() => writers.length > 0)() && readers.length > 0;
160
+ },
161
+ children: " · "
162
+ }), _$createComponent(Show, {
163
+ get when() {
164
+ return readers.length > 0;
165
+ },
166
+ get children() {
167
+ return t().readingAlong({
168
+ names: listNames(readers.map(short), t().and),
169
+ count: readers.length
170
+ });
171
+ }
172
+ })];
173
+ };
174
+ const chip = person => {
175
+ const content = [_$createComponent(PresenceFace, {
176
+ person: person,
177
+ get live() {
178
+ return state() === "live";
179
+ }
180
+ }), short(person), _$ssr(_tmpl$4, _$ssrAttribute("class", person.activity === "writing" ? "ti ti-pencil k2b-detail-presence__activity" : "ti ti-eye k2b-detail-presence__activity", false) + _$ssrAttribute("data-activity", _$escape(person.activity ?? "reading", true), false))];
181
+ return _$createComponent(Show, {
182
+ get when() {
183
+ return _$memo(() => !!person.onSelect)() && state() === "live";
184
+ },
185
+ get fallback() {
186
+ return _$ssr(_tmpl$6, _$ssrStyleProperty("--k2b-person-color:", _$escape(color(person), true)), _$ssrAttribute("title", _$escape(describe(person), true), false), _$escape(describe(person)), _$escape(content));
187
+ },
188
+ get children() {
189
+ return _$ssr(_tmpl$5, _$ssrStyleProperty("--k2b-person-color:", _$escape(color(person), true)), _$ssrAttribute("title", _$escape(describe(person), true), false) + _$ssrAttribute("aria-label", _$escape(t().jumpTo({
190
+ name: person.name,
191
+ place: person.place ?? ""
192
+ }), true), false), _$escape(content));
193
+ }
194
+ });
195
+ };
196
+ const count = () => (state() === "live" || state() === "offline") && others().length > 0 ? others().length : undefined;
197
+ const meta = () => state() === "live" && others().length === 0 ? t().onlyYou : undefined;
198
+ return _$createComponent(DetailPanel.Section, {
199
+ get title() {
200
+ return props.title ?? t().hereNow;
201
+ },
202
+ icon: "ti ti-users",
203
+ get count() {
204
+ return count();
205
+ },
206
+ get meta() {
207
+ return meta();
208
+ },
209
+ get actions() {
210
+ return props.actions;
211
+ },
212
+ get ["class"]() {
213
+ return props.class;
214
+ },
215
+ get children() {
216
+ return _$ssr(_tmpl$9, _$ssrAttribute("data-state", _$escape(state(), true), false) + _$ssrAttribute("aria-busy", state() === "loading" ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
217
+ get when() {
218
+ return _$memo(() => state() !== "loading")() && state() !== "unavailable";
219
+ },
220
+ get fallback() {
221
+ return _$createComponent(Show, {
222
+ get when() {
223
+ return state() === "unavailable";
224
+ },
225
+ get fallback() {
226
+ return [_$ssr(_tmpl$1), _$ssr(_tmpl$10, _$escape(t().presenceLoading))];
227
+ },
228
+ get children() {
229
+ return [_$ssr(_tmpl$0, _$escape(t().presenceUnavailable), _$escape(_$createComponent(Show, {
230
+ get when() {
231
+ return props.onRetry;
232
+ },
233
+ children: retry => _$createComponent(Button, {
234
+ variant: "text",
235
+ size: "xs",
236
+ "class": "k2b-detail-presence__retry",
237
+ onClick: () => retry()(),
238
+ get children() {
239
+ return [_$ssr(_tmpl$11), " ", t().retry];
240
+ }
241
+ })
242
+ }))), _$ssr(_tmpl$8, _$escape(props.note))];
243
+ }
244
+ });
245
+ },
246
+ get children() {
247
+ return [_$ssr(_tmpl$7, _$escape(_$createComponent(For, {
248
+ get each() {
249
+ return others();
250
+ },
251
+ children: person => chip(person)
252
+ })), _$escape(_$createComponent(AvatarStack, {
253
+ "class": "k2b-detail-presence__all",
254
+ get people() {
255
+ return listPeople();
256
+ },
257
+ get title() {
258
+ return props.title ?? t().hereNow;
259
+ },
260
+ get label() {
261
+ return t().everyoneHere({
262
+ count: props.people.length
263
+ });
264
+ },
265
+ get hint() {
266
+ return props.listHint;
267
+ },
268
+ get trigger() {
269
+ return [_$ssr(_tmpl$12, _$ssrAttribute("data-empty", hidden() === 0 ? "true" : _$escape(undefined, true), false), _$escape(hidden())), _$createComponent(Show, {
270
+ get when() {
271
+ return self();
272
+ },
273
+ children: person => _$ssr(_tmpl$13, _$escape(_$createComponent(PresenceFace, {
274
+ get person() {
275
+ return person();
276
+ },
277
+ live: false
278
+ })), _$escape(t().you))
279
+ })];
280
+ }
281
+ }))), _$ssr(_tmpl$8, _$escape(where()))];
282
+ }
283
+ })));
284
+ }
285
+ });
286
+ }
287
+
288
+ /** A fixed slot at the end of a row for the people working there, such as an outline heading. Always the same width. */
289
+ export function DetailPresenceSlot(props) {
290
+ const max = () => Math.max(1, props.max ?? 2);
291
+ return _$ssr(_tmpl$15, _$escape(_$createComponent(For, {
292
+ get each() {
293
+ return props.people.slice(0, max());
294
+ },
295
+ children: person => _$createComponent(Avatar, {
296
+ get name() {
297
+ return person.name;
298
+ },
299
+ get src() {
300
+ return person.src;
301
+ },
302
+ get icon() {
303
+ return person.icon;
304
+ },
305
+ get shape() {
306
+ return person.shape;
307
+ },
308
+ size: "xs",
309
+ alt: ""
310
+ })
311
+ })), _$escape(_$createComponent(Show, {
312
+ get when() {
313
+ return props.people.length > max();
314
+ },
315
+ get children() {
316
+ return _$ssr(_tmpl$14, _$escape(props.people.length) - _$escape(max()));
317
+ }
318
+ })));
319
+ }
320
+
321
+ // ── Access ──────────────────────────────────────────────────────────────
322
+
323
+ const ACCESS_ICONS = {
324
+ person: undefined,
325
+ service: undefined,
326
+ group: "ti ti-users-group",
327
+ everyone: "ti ti-users",
328
+ public: "ti ti-world"
329
+ };
330
+
331
+ /**
332
+ * Who has access and with which role, read-only. Closed it names the first entries; open it lists `limit` rows and
333
+ * "Show n more". A list that may load later passes `state="loading"` and keeps its place.
334
+ */
335
+ export function DetailAccess(props) {
336
+ const t = useDetailSectionMessages();
337
+ const panel = useDetailPanelMessages();
338
+ const [all, setAll] = createSignal(false);
339
+ const limit = () => Math.max(1, props.limit ?? 5);
340
+ const state = () => props.state ?? "ready";
341
+ const shown = () => all() ? props.entries : props.entries.slice(0, limit());
342
+ const hiddenCount = () => Math.max(0, props.entries.length - limit());
343
+ const nameOf = entry => entry.self ? t().you : entry.name;
344
+ const summary = () => {
345
+ if (state() === "loading") return t().accessLoading;
346
+ if (state() === "error") return t().accessFailed;
347
+ const names = props.entries.slice(0, 2).map(nameOf);
348
+ return names.length ? t().accessSummary({
349
+ names: names.join(", "),
350
+ more: Math.max(0, props.entries.length - 2)
351
+ }) : undefined;
352
+ };
353
+ const leading = entry => {
354
+ const kind = entry.kind ?? "person";
355
+ return _$createComponent(Avatar, {
356
+ get name() {
357
+ return entry.name;
358
+ },
359
+ get src() {
360
+ return kind === "person" ? entry.src : undefined;
361
+ },
362
+ get icon() {
363
+ return ACCESS_ICONS[kind];
364
+ },
365
+ shape: kind === "person" ? "circle" : "square",
366
+ size: "xs",
367
+ alt: ""
368
+ });
369
+ };
370
+ return _$createComponent(DetailPanel.Section, {
371
+ get title() {
372
+ return props.title ?? t().access;
373
+ },
374
+ icon: "ti ti-lock-access",
375
+ get count() {
376
+ return _$memo(() => state() === "ready")() ? props.entries.length : undefined;
377
+ },
378
+ collapsible: true,
379
+ get summary() {
380
+ return summary();
381
+ },
382
+ get defaultOpen() {
383
+ return props.defaultOpen;
384
+ },
385
+ get open() {
386
+ return props.open;
387
+ },
388
+ get onOpenChange() {
389
+ return props.onOpenChange;
390
+ },
391
+ get ["class"]() {
392
+ return props.class;
393
+ },
394
+ get children() {
395
+ return _$ssr(_tmpl$19, _$ssrAttribute("aria-busy", state() === "loading" ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
396
+ get when() {
397
+ return state() === "loading";
398
+ },
399
+ get children() {
400
+ return [_$createComponent(For, {
401
+ each: [0, 1, 2],
402
+ children: () => _$createComponent(DetailPanel.Row, {
403
+ get leading() {
404
+ return _$ssr(_tmpl$20);
405
+ },
406
+ get title() {
407
+ return _$ssr(_tmpl$21);
408
+ }
409
+ })
410
+ }), _$ssr(_tmpl$16, _$escape(t().accessLoading))];
411
+ }
412
+ })), _$escape(_$createComponent(Show, {
413
+ get when() {
414
+ return state() === "error";
415
+ },
416
+ get children() {
417
+ return _$ssr(_tmpl$17, _$escape(t().accessFailed), _$escape(_$createComponent(Show, {
418
+ get when() {
419
+ return props.onRetry;
420
+ },
421
+ children: retry => _$createComponent(Button, {
422
+ variant: "text",
423
+ size: "xs",
424
+ onClick: () => retry()(),
425
+ get children() {
426
+ return t().retry;
427
+ }
428
+ })
429
+ })));
430
+ }
431
+ })), _$escape(_$createComponent(Show, {
432
+ get when() {
433
+ return state() === "ready";
434
+ },
435
+ get children() {
436
+ return [_$createComponent(For, {
437
+ get each() {
438
+ return shown();
439
+ },
440
+ children: entry => _$createComponent(DetailPanel.Row, {
441
+ get leading() {
442
+ return leading(entry);
443
+ },
444
+ get title() {
445
+ return nameOf(entry);
446
+ },
447
+ get description() {
448
+ return entry.detail;
449
+ },
450
+ get trailing() {
451
+ return entry.role;
452
+ }
453
+ })
454
+ }), _$createComponent(Show, {
455
+ get when() {
456
+ return hiddenCount() > 0;
457
+ },
458
+ get children() {
459
+ return _$createComponent(DetailPanel.Action, {
460
+ type: "button",
461
+ get ["aria-expanded"]() {
462
+ return all();
463
+ },
464
+ get leading() {
465
+ return _$ssr(_tmpl$4, _$ssrAttribute("class", all() ? "ti ti-chevron-up" : "ti ti-chevron-down", false));
466
+ },
467
+ get title() {
468
+ return _$memo(() => !!all())() ? panel().showLess : panel().showMore({
469
+ count: hiddenCount()
470
+ });
471
+ },
472
+ onClick: () => setAll(!all())
473
+ });
474
+ }
475
+ })];
476
+ }
477
+ })), _$escape(_$createComponent(Show, {
478
+ get when() {
479
+ return props.manage;
480
+ },
481
+ children: manage => _$createComponent(Show, {
482
+ get when() {
483
+ return manage().href;
484
+ },
485
+ get fallback() {
486
+ return _$createComponent(DetailPanel.Action, {
487
+ type: "button",
488
+ get leading() {
489
+ return _$ssr(_tmpl$22);
490
+ },
491
+ get title() {
492
+ return manage().label;
493
+ },
494
+ onClick: () => manage().onClick?.()
495
+ });
496
+ },
497
+ children: href => _$createComponent(DetailPanel.Action, {
498
+ get href() {
499
+ return href();
500
+ },
501
+ get leading() {
502
+ return _$ssr(_tmpl$22);
503
+ },
504
+ get title() {
505
+ return manage().label;
506
+ }
507
+ })
508
+ })
509
+ })), _$escape(_$createComponent(Show, {
510
+ get when() {
511
+ return props.note;
512
+ },
513
+ get children() {
514
+ return _$ssr(_tmpl$18, _$escape(props.note));
515
+ }
516
+ })));
517
+ }
518
+ });
519
+ }
520
+
521
+ // ── Links ───────────────────────────────────────────────────────────────
522
+
523
+ const LinkRow = props => {
524
+ const item = props.item;
525
+ const menu = item.menuItems ? {
526
+ menuItems: item.menuItems,
527
+ menuLabel: item.menuLabel
528
+ } : {};
529
+ const shared = {
530
+ leading: item.leading ?? _$ssr(_tmpl$4, _$ssrAttribute("class", _$escape(item.icon ?? "ti ti-file-text", true), false)),
531
+ title: item.title,
532
+ description: item.description,
533
+ trailing: item.trailing,
534
+ ...menu
535
+ };
536
+ return _$createComponent(Show, {
537
+ get when() {
538
+ return _$memo(() => !!!item.disabled)() && item.href;
539
+ },
540
+ get fallback() {
541
+ return _$createComponent(DetailPanel.Action, _$mergeProps({
542
+ type: "button",
543
+ get disabled() {
544
+ return item.disabled;
545
+ },
546
+ onClick: event => item.onClick?.(event)
547
+ }, shared));
548
+ },
549
+ children: href => _$createComponent(DetailPanel.Action, _$mergeProps({
550
+ get href() {
551
+ return href();
552
+ },
553
+ get target() {
554
+ return item.target;
555
+ },
556
+ get rel() {
557
+ return item.rel;
558
+ },
559
+ onClick: event => item.onClick?.(event)
560
+ }, shared))
561
+ });
562
+ };
563
+ const LinkGroup = props => {
564
+ const panel = useDetailPanelMessages();
565
+ const [all, setAll] = createSignal(false);
566
+ const hiddenCount = () => Math.max(0, props.items.length - props.limit);
567
+ return _$ssr(_tmpl$23, _$escape(props.label), _$escape(_$createComponent(For, {
568
+ get each() {
569
+ return _$memo(() => !!all())() ? props.items : props.items.slice(0, props.limit);
570
+ },
571
+ children: item => _$createComponent(LinkRow, {
572
+ item: item
573
+ })
574
+ })), _$escape(_$createComponent(Show, {
575
+ get when() {
576
+ return hiddenCount() > 0;
577
+ },
578
+ get children() {
579
+ return _$createComponent(DetailPanel.Action, {
580
+ type: "button",
581
+ get ["aria-expanded"]() {
582
+ return all();
583
+ },
584
+ get leading() {
585
+ return _$ssr(_tmpl$4, _$ssrAttribute("class", all() ? "ti ti-chevron-up" : "ti ti-chevron-down", false));
586
+ },
587
+ get title() {
588
+ return _$memo(() => !!all())() ? panel().showLess : panel().showMore({
589
+ count: hiddenCount()
590
+ });
591
+ },
592
+ onClick: () => setAll(!all())
593
+ });
594
+ }
595
+ })));
596
+ };
597
+
598
+ /**
599
+ * "Linked from" and "Links to" in one section. Without links it is one row: the title and "None yet", followed by the
600
+ * `footer` when there is one.
601
+ */
602
+ export function DetailLinks(props) {
603
+ const t = useDetailSectionMessages();
604
+ const limit = () => Math.max(1, props.limit ?? 5);
605
+ const state = () => props.state ?? "ready";
606
+ const total = () => props.incoming.length + props.outgoing.length;
607
+ // Resolve once: the empty state reads it as a condition and as content.
608
+ const footer = children(() => props.footer);
609
+ const summary = () => {
610
+ if (state() === "loading") return t().linksLoading;
611
+ if (state() === "error") return t().linksFailed;
612
+ const names = items => ({
613
+ names: listNames(items.slice(0, 2).map(item => item.title), t().and),
614
+ more: Math.max(0, items.length - 2)
615
+ });
616
+ return props.incoming.length ? t().linkedBySummary(names(props.incoming)) : t().linksToSummary(names(props.outgoing));
617
+ };
618
+ // Loading and failure take the row the next links would take, so the loaded rows above never move.
619
+ const pending = () => [_$createComponent(Show, {
620
+ get when() {
621
+ return state() === "loading";
622
+ },
623
+ get children() {
624
+ return _$ssr(_tmpl$24, _$escape(_$createComponent(DetailPanel.Row, {
625
+ get leading() {
626
+ return _$ssr(_tmpl$20);
627
+ },
628
+ get title() {
629
+ return _$ssr(_tmpl$21);
630
+ }
631
+ })), _$escape(t().linksLoading));
632
+ }
633
+ }), _$createComponent(Show, {
634
+ get when() {
635
+ return state() === "error";
636
+ },
637
+ get children() {
638
+ return _$ssr(_tmpl$17, _$escape(t().linksFailed), _$escape(_$createComponent(Show, {
639
+ get when() {
640
+ return props.onRetry;
641
+ },
642
+ children: retry => _$createComponent(Button, {
643
+ variant: "text",
644
+ size: "xs",
645
+ onClick: () => retry()(),
646
+ get children() {
647
+ return t().retry;
648
+ }
649
+ })
650
+ })));
651
+ }
652
+ })];
653
+ return _$createComponent(Show, {
654
+ get when() {
655
+ return total() > 0;
656
+ },
657
+ get fallback() {
658
+ return _$createComponent(DetailPanel.Section, {
659
+ get title() {
660
+ return props.title ?? t().links;
661
+ },
662
+ icon: "ti ti-arrows-exchange",
663
+ get count() {
664
+ return state() === "ready" ? 0 : undefined;
665
+ },
666
+ get ["class"]() {
667
+ return props.class;
668
+ },
669
+ get children() {
670
+ return _$createComponent(Show, {
671
+ get when() {
672
+ return state() !== "ready" || footer();
673
+ },
674
+ get children() {
675
+ return _$ssr(_tmpl$26, _$escape(pending()), _$escape(footer()));
676
+ }
677
+ });
678
+ }
679
+ });
680
+ },
681
+ get children() {
682
+ return _$createComponent(DetailPanel.Section, {
683
+ get title() {
684
+ return props.title ?? t().links;
685
+ },
686
+ icon: "ti ti-arrows-exchange",
687
+ get count() {
688
+ return _$memo(() => state() === "ready")() ? total() : undefined;
689
+ },
690
+ collapsible: true,
691
+ get defaultOpen() {
692
+ return props.defaultOpen ?? true;
693
+ },
694
+ get summary() {
695
+ return summary();
696
+ },
697
+ get ["class"]() {
698
+ return props.class;
699
+ },
700
+ get children() {
701
+ return _$ssr(_tmpl$25, _$escape(_$createComponent(Show, {
702
+ get when() {
703
+ return props.incoming.length > 0;
704
+ },
705
+ get children() {
706
+ return _$createComponent(LinkGroup, {
707
+ get label() {
708
+ return t().linkedBy({
709
+ count: props.incoming.length
710
+ });
711
+ },
712
+ get items() {
713
+ return props.incoming;
714
+ },
715
+ get limit() {
716
+ return limit();
717
+ }
718
+ });
719
+ }
720
+ })), _$escape(_$createComponent(Show, {
721
+ get when() {
722
+ return props.outgoing.length > 0;
723
+ },
724
+ get children() {
725
+ return _$createComponent(LinkGroup, {
726
+ get label() {
727
+ return t().linksTo({
728
+ count: props.outgoing.length
729
+ });
730
+ },
731
+ get items() {
732
+ return props.outgoing;
733
+ },
734
+ get limit() {
735
+ return limit();
736
+ }
737
+ });
738
+ }
739
+ })), _$escape(pending()), _$escape(footer()));
740
+ }
741
+ });
742
+ }
743
+ });
744
+ }
745
+
746
+ // ── Properties ──────────────────────────────────────────────────────────
747
+
748
+ /** Facts about the object as label and value rows, such as created, edited, size, and the way to its history. */
749
+ export function DetailProperties(props) {
750
+ const t = useDetailSectionMessages();
751
+ return _$createComponent(DetailPanel.Section, {
752
+ get title() {
753
+ return props.title ?? t().properties;
754
+ },
755
+ get icon() {
756
+ return props.icon ?? "ti ti-info-square";
757
+ },
758
+ collapsible: true,
759
+ get defaultOpen() {
760
+ return props.defaultOpen;
761
+ },
762
+ get summary() {
763
+ return props.summary;
764
+ },
765
+ get ["class"]() {
766
+ return props.class;
767
+ },
768
+ get children() {
769
+ return [_$ssr(_tmpl$27, _$escape(_$createComponent(For, {
770
+ get each() {
771
+ return props.items;
772
+ },
773
+ children: item => _$ssr(_tmpl$29, _$escape(item.label), _$escape(_$createComponent(Show, {
774
+ get when() {
775
+ return item.leading;
776
+ },
777
+ get children() {
778
+ return _$ssr(_tmpl$28, _$escape(item.leading));
779
+ }
780
+ })), _$escape(_$createComponent(Show, {
781
+ get when() {
782
+ return item.href;
783
+ },
784
+ get fallback() {
785
+ return _$ssr(_tmpl$30, _$escape(item.value));
786
+ },
787
+ children: href => _$ssr(_tmpl$31, _$ssrAttribute("href", _$escape(href(), true), false), _$escape(item.value))
788
+ })))
789
+ }))), props.children];
790
+ }
791
+ });
792
+ }
793
+
794
+ // ── Tags ────────────────────────────────────────────────────────────────
795
+
796
+ /** The object's tags as chips; a chip with `href` leads to everything with that tag. */
797
+ export function DetailTags(props) {
798
+ const t = useDetailSectionMessages();
799
+ return _$createComponent(DetailPanel.Section, {
800
+ get title() {
801
+ return props.title ?? t().tags;
802
+ },
803
+ icon: "ti ti-hash",
804
+ get count() {
805
+ return props.tags.length;
806
+ },
807
+ get ["class"]() {
808
+ return props.class;
809
+ },
810
+ get children() {
811
+ return _$createComponent(Show, {
812
+ get when() {
813
+ return props.tags.length > 0;
814
+ },
815
+ get fallback() {
816
+ return _$memo(() => !!props.emptyHint)() ? _$ssr(_tmpl$18, _$escape(props.emptyHint)) : undefined;
817
+ },
818
+ get children() {
819
+ return _$ssr(_tmpl$32, _$escape(_$createComponent(For, {
820
+ get each() {
821
+ return props.tags;
822
+ },
823
+ children: tag => _$ssr(_tmpl$33, _$escape(_$createComponent(Show, {
824
+ get when() {
825
+ return tag.href;
826
+ },
827
+ get fallback() {
828
+ return _$ssr(_tmpl$34, _$escape(tag.label));
829
+ },
830
+ children: href => _$ssr(_tmpl$35, _$ssrAttribute("href", _$escape(href(), true), false), _$escape(tag.label))
831
+ })))
832
+ })));
833
+ }
834
+ });
835
+ }
836
+ });
837
+ }
838
+ //# sourceMappingURL=DetailSections.js.map