@riducms/admin 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/THIRD_PARTY_NOTICES.md +217 -0
  4. package/package.json +62 -0
  5. package/src/app/admin-core-view-route.svelte +89 -0
  6. package/src/app/admin-layout.svelte +457 -0
  7. package/src/app/admin-navigation-progress.svelte +204 -0
  8. package/src/app/admin-page-title.svelte +94 -0
  9. package/src/app/admin-provider-layer.svelte +26 -0
  10. package/src/app/admin-router-root.svelte +9 -0
  11. package/src/app/admin-routes.svelte +116 -0
  12. package/src/app/deferred-redirect.svelte +23 -0
  13. package/src/app/loading-boundary.svelte +70 -0
  14. package/src/app/not-found-route.svelte +29 -0
  15. package/src/app-meta.ts +2 -0
  16. package/src/app.css +467 -0
  17. package/src/app.svelte +88 -0
  18. package/src/assets/ridu-logo-arch.svg +3 -0
  19. package/src/assets/ridu-word-logo.svg +7 -0
  20. package/src/components/brand/ridu-logo.svelte +24 -0
  21. package/src/components/code-block/code-block.svelte +107 -0
  22. package/src/components/json-tree/json-tree-node.svelte +77 -0
  23. package/src/components/json-tree/json-viewer.svelte +113 -0
  24. package/src/components/ui/banner/banner.svelte +41 -0
  25. package/src/components/ui/banner/index.ts +5 -0
  26. package/src/components/ui/bulk-action-bar/bulk-action-bar.svelte +50 -0
  27. package/src/components/ui/bulk-action-bar/index.ts +1 -0
  28. package/src/components/ui/button/index.ts +8 -0
  29. package/src/components/ui/calendar/calendar-caption.svelte +72 -0
  30. package/src/components/ui/calendar/calendar-cell.svelte +20 -0
  31. package/src/components/ui/calendar/calendar-day.svelte +27 -0
  32. package/src/components/ui/calendar/calendar-grid-row.svelte +13 -0
  33. package/src/components/ui/calendar/calendar-grid.svelte +17 -0
  34. package/src/components/ui/calendar/calendar-head-cell.svelte +20 -0
  35. package/src/components/ui/calendar/calendar-header.svelte +20 -0
  36. package/src/components/ui/calendar/calendar-heading.svelte +17 -0
  37. package/src/components/ui/calendar/calendar-month-select.svelte +45 -0
  38. package/src/components/ui/calendar/calendar-month.svelte +16 -0
  39. package/src/components/ui/calendar/calendar-months.svelte +20 -0
  40. package/src/components/ui/calendar/calendar-nav.svelte +20 -0
  41. package/src/components/ui/calendar/calendar-next-button.svelte +30 -0
  42. package/src/components/ui/calendar/calendar-prev-button.svelte +30 -0
  43. package/src/components/ui/calendar/calendar-year-select.svelte +44 -0
  44. package/src/components/ui/calendar/calendar.svelte +118 -0
  45. package/src/components/ui/calendar/index.ts +1 -0
  46. package/src/components/ui/checkbox/checkbox.svelte +35 -0
  47. package/src/components/ui/checkbox/index.ts +1 -0
  48. package/src/components/ui/confirmation-dialog/confirmation-dialog.svelte +37 -0
  49. package/src/components/ui/confirmation-dialog/index.ts +1 -0
  50. package/src/components/ui/date-value-control/date-value-calendar.svelte +37 -0
  51. package/src/components/ui/date-value-control/date-value-control-loader.svelte +36 -0
  52. package/src/components/ui/date-value-control/date-value-control.svelte +177 -0
  53. package/src/components/ui/date-value-control/index.ts +3 -0
  54. package/src/components/ui/dialog/dialog-close.svelte +11 -0
  55. package/src/components/ui/dialog/dialog-content.svelte +52 -0
  56. package/src/components/ui/dialog/dialog-description.svelte +20 -0
  57. package/src/components/ui/dialog/dialog-footer.svelte +20 -0
  58. package/src/components/ui/dialog/dialog-header.svelte +20 -0
  59. package/src/components/ui/dialog/dialog-overlay.svelte +20 -0
  60. package/src/components/ui/dialog/dialog-title.svelte +17 -0
  61. package/src/components/ui/dialog/dialog-trigger.svelte +11 -0
  62. package/src/components/ui/dialog/dialog.svelte +7 -0
  63. package/src/components/ui/dialog/index.ts +8 -0
  64. package/src/components/ui/dropdown-menu/dropdown-menu-content.svelte +27 -0
  65. package/src/components/ui/dropdown-menu/dropdown-menu-item.svelte +20 -0
  66. package/src/components/ui/dropdown-menu/dropdown-menu-separator.svelte +17 -0
  67. package/src/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +16 -0
  68. package/src/components/ui/dropdown-menu/dropdown-menu.svelte +7 -0
  69. package/src/components/ui/dropdown-menu/index.ts +5 -0
  70. package/src/components/ui/empty-state/empty-state.svelte +34 -0
  71. package/src/components/ui/empty-state/index.ts +1 -0
  72. package/src/components/ui/input/index.ts +1 -0
  73. package/src/components/ui/kbd/index.ts +1 -0
  74. package/src/components/ui/kbd/kbd.svelte +24 -0
  75. package/src/components/ui/pagination/index.ts +1 -0
  76. package/src/components/ui/pagination/pagination.svelte +88 -0
  77. package/src/components/ui/popover/index.ts +3 -0
  78. package/src/components/ui/popover/popover-content.svelte +29 -0
  79. package/src/components/ui/popover/popover-trigger.svelte +11 -0
  80. package/src/components/ui/popover/popover.svelte +7 -0
  81. package/src/components/ui/radio-group/index.ts +3 -0
  82. package/src/components/ui/radio-group/radio-card-item.svelte +38 -0
  83. package/src/components/ui/radio-group/radio-group-item.svelte +17 -0
  84. package/src/components/ui/radio-group/radio-group.svelte +19 -0
  85. package/src/components/ui/select/index.ts +4 -0
  86. package/src/components/ui/select/select-content.svelte +38 -0
  87. package/src/components/ui/select/select-item.svelte +40 -0
  88. package/src/components/ui/select/select-scroll-down-button.svelte +23 -0
  89. package/src/components/ui/select/select-scroll-up-button.svelte +23 -0
  90. package/src/components/ui/select/select-trigger.svelte +29 -0
  91. package/src/components/ui/select/select.svelte +11 -0
  92. package/src/components/ui/sheet/index.ts +6 -0
  93. package/src/components/ui/sheet/sheet-close.svelte +7 -0
  94. package/src/components/ui/sheet/sheet-content.svelte +77 -0
  95. package/src/components/ui/sheet/sheet-description.svelte +17 -0
  96. package/src/components/ui/sheet/sheet-overlay.svelte +36 -0
  97. package/src/components/ui/sheet/sheet-title.svelte +17 -0
  98. package/src/components/ui/sheet/sheet-trigger.svelte +7 -0
  99. package/src/components/ui/sheet/sheet.svelte +7 -0
  100. package/src/components/ui/skeleton/index.ts +1 -0
  101. package/src/components/ui/skeleton/skeleton.svelte +31 -0
  102. package/src/components/ui/slider/index.ts +3 -0
  103. package/src/components/ui/slider/slider-content.svelte +59 -0
  104. package/src/components/ui/slider/slider.svelte +35 -0
  105. package/src/components/ui/sonner/index.ts +1 -0
  106. package/src/components/ui/sonner/sonner.svelte +55 -0
  107. package/src/components/ui/status-indicator/index.ts +5 -0
  108. package/src/components/ui/status-indicator/status-indicator.svelte +54 -0
  109. package/src/components/ui/table/index.ts +6 -0
  110. package/src/components/ui/table/table-body.svelte +20 -0
  111. package/src/components/ui/table/table-cell.svelte +23 -0
  112. package/src/components/ui/table/table-head.svelte +23 -0
  113. package/src/components/ui/table/table-header.svelte +20 -0
  114. package/src/components/ui/table/table-row.svelte +23 -0
  115. package/src/components/ui/table/table.svelte +22 -0
  116. package/src/components/ui/tabs/index.ts +8 -0
  117. package/src/components/ui/tabs/tabs-content.svelte +17 -0
  118. package/src/components/ui/tabs/tabs-list.svelte +40 -0
  119. package/src/components/ui/tabs/tabs-trigger.svelte +23 -0
  120. package/src/components/ui/tabs/tabs.svelte +19 -0
  121. package/src/components/ui/textarea/index.ts +1 -0
  122. package/src/components/ui/tooltip/index.ts +4 -0
  123. package/src/components/ui/tooltip/tooltip-content.svelte +50 -0
  124. package/src/components/ui/tooltip/tooltip-provider.svelte +7 -0
  125. package/src/components/ui/tooltip/tooltip-trigger.svelte +7 -0
  126. package/src/components/ui/tooltip/tooltip.svelte +7 -0
  127. package/src/core/api/admin-client.ts +45 -0
  128. package/src/core/forms/field-condition.ts +63 -0
  129. package/src/core/forms/form-controller.svelte.ts +369 -0
  130. package/src/core/forms/form-draft-recovery.ts +80 -0
  131. package/src/core/forms/form-schema.ts +609 -0
  132. package/src/core/forms/form-validation.ts +750 -0
  133. package/src/core/i18n/admin-i18n.svelte.ts +364 -0
  134. package/src/core/i18n/localized-schema.ts +111 -0
  135. package/src/core/notifications/notification-center.svelte.ts +69 -0
  136. package/src/core/notifications/validation-toast.svelte +18 -0
  137. package/src/core/plugins/field-registry.ts +60 -0
  138. package/src/core/plugins/row-label-registry.ts +62 -0
  139. package/src/core/preferences/preference-write-queue.ts +107 -0
  140. package/src/core/routing/admin-paths.ts +137 -0
  141. package/src/core/runtime/admin-runtime.svelte.ts +625 -0
  142. package/src/features/auth/account-menu.svelte +159 -0
  143. package/src/features/auth/account-navigation.svelte +44 -0
  144. package/src/features/auth/account-route.svelte +395 -0
  145. package/src/features/auth/account-security-controller.svelte.ts +119 -0
  146. package/src/features/auth/account-security-route.svelte +460 -0
  147. package/src/features/auth/auth-brand.svelte +17 -0
  148. package/src/features/auth/auth-frame.svelte +18 -0
  149. package/src/features/auth/create-first-user-controller.svelte.ts +220 -0
  150. package/src/features/auth/create-first-user-route.svelte +174 -0
  151. package/src/features/auth/forgot-password-route.svelte +78 -0
  152. package/src/features/auth/login-route.svelte +221 -0
  153. package/src/features/auth/request-verification-route.svelte +73 -0
  154. package/src/features/auth/reset-password-route.svelte +107 -0
  155. package/src/features/auth/verify-email-route.svelte +60 -0
  156. package/src/features/collections/collection-list-bulk-actions.svelte +143 -0
  157. package/src/features/collections/collection-list-bulk-editor.svelte +157 -0
  158. package/src/features/collections/collection-list-controller.svelte.ts +717 -0
  159. package/src/features/collections/collection-list-filter-popover.svelte +238 -0
  160. package/src/features/collections/collection-list-preferences-controller.svelte.ts +393 -0
  161. package/src/features/collections/collection-list-results.svelte +410 -0
  162. package/src/features/collections/collection-list-route.svelte +951 -0
  163. package/src/features/collections/collection-list-workspace-toolbar.svelte +409 -0
  164. package/src/features/collections/collection-table-skeleton.svelte +44 -0
  165. package/src/features/collections/list-workspace.ts +265 -0
  166. package/src/features/dashboard/dashboard-route.svelte +141 -0
  167. package/src/features/development/development-tools.svelte +217 -0
  168. package/src/features/documents/document-api-view-loader.svelte +31 -0
  169. package/src/features/documents/document-api-view.svelte +547 -0
  170. package/src/features/documents/document-controller.svelte.ts +1252 -0
  171. package/src/features/documents/document-field-partition.ts +18 -0
  172. package/src/features/documents/document-field-sections.svelte +46 -0
  173. package/src/features/documents/document-heading.ts +20 -0
  174. package/src/features/documents/document-route-dirty.ts +43 -0
  175. package/src/features/documents/document-route-loader.svelte +22 -0
  176. package/src/features/documents/document-route.svelte +1124 -0
  177. package/src/features/documents/live-preview-panel.svelte +390 -0
  178. package/src/features/documents/live-preview.ts +61 -0
  179. package/src/features/documents/upload-document-preview-loader.svelte +40 -0
  180. package/src/features/documents/upload-document-preview.svelte +525 -0
  181. package/src/features/localization/content-locale-switcher.svelte +93 -0
  182. package/src/features/navigation/admin-breadcrumbs.svelte +124 -0
  183. package/src/features/navigation/admin-command-menu.svelte +245 -0
  184. package/src/features/navigation/admin-resource-groups.ts +72 -0
  185. package/src/features/reference-browser/reference-browser-loader.svelte +15 -0
  186. package/src/features/reference-browser/reference-browser-workflow.svelte.ts +691 -0
  187. package/src/features/reference-browser/reference-browser.svelte +844 -0
  188. package/src/features/uploads/bulk-upload-route.svelte +624 -0
  189. package/src/features/uploads/bulk-upload.ts +39 -0
  190. package/src/features/uploads/upload-metadata-fields.svelte +112 -0
  191. package/src/features/versions/version-diff.ts +72 -0
  192. package/src/features/versions/version-history-route.svelte +581 -0
  193. package/src/fields/field-clipboard.ts +132 -0
  194. package/src/fields/field-layout.svelte +60 -0
  195. package/src/fields/field-layout.ts +57 -0
  196. package/src/fields/field-messages.svelte +29 -0
  197. package/src/fields/field-renderer.svelte +152 -0
  198. package/src/fields/field-shell.svelte +30 -0
  199. package/src/fields/field-tab-layout.svelte +99 -0
  200. package/src/fields/join/join-field.svelte +358 -0
  201. package/src/fields/localized-field-presentation.ts +17 -0
  202. package/src/fields/nested/nested-field.svelte +602 -0
  203. package/src/fields/nested/scoped-field.ts +78 -0
  204. package/src/fields/nested/sortable-row.svelte +32 -0
  205. package/src/fields/point/point-field.svelte +52 -0
  206. package/src/fields/radio/radio-field.svelte +34 -0
  207. package/src/fields/relationship/relationship-field-controller.svelte.ts +245 -0
  208. package/src/fields/relationship/relationship-field.svelte +258 -0
  209. package/src/fields/relationship/relationship-lookup-controller.svelte.ts +129 -0
  210. package/src/fields/relationship/relationship-query.ts +88 -0
  211. package/src/fields/relationship/relationship-quick-picker-controller.svelte.ts +198 -0
  212. package/src/fields/relationship/relationship-quick-picker.svelte +327 -0
  213. package/src/fields/relationship/relationship-value.ts +47 -0
  214. package/src/fields/scalar/date-control-value.ts +60 -0
  215. package/src/fields/scalar/date-value.ts +26 -0
  216. package/src/fields/scalar/scalar-field.svelte +137 -0
  217. package/src/fields/select/select-field.svelte +132 -0
  218. package/src/fields/select/select-value.ts +14 -0
  219. package/src/fields/text/slug-field.svelte +87 -0
  220. package/src/fields/text/slug.ts +38 -0
  221. package/src/fields/text/text-field.svelte +41 -0
  222. package/src/fields/ui/ui-field.svelte +12 -0
  223. package/src/fields/virtual/virtual-field.svelte +28 -0
  224. package/src/index.ts +49 -0
  225. package/src/main.ts +12 -0
  226. package/src/vite-env.d.ts +9 -0
@@ -0,0 +1,409 @@
1
+ <script lang="ts">
2
+ import type { SchemaField } from "@riducms/protocol";
3
+ import type { Snippet } from "svelte";
4
+ import Columns3Icon from "~icons/lucide/columns-3";
5
+ import ListTreeIcon from "~icons/lucide/list-tree";
6
+ import SaveIcon from "~icons/lucide/save";
7
+ import SearchIcon from "~icons/lucide/search";
8
+ import SlidersHorizontalIcon from "~icons/lucide/sliders-horizontal";
9
+ import XIcon from "~icons/lucide/x";
10
+
11
+ import { Button, buttonVariants } from "@admin/components/ui/button";
12
+ import { Checkbox } from "@admin/components/ui/checkbox";
13
+ import { Input } from "@admin/components/ui/input";
14
+ import { Popover, PopoverContent, PopoverTrigger } from "@admin/components/ui/popover";
15
+ import { Select, SelectContent, SelectItem, SelectTrigger } from "@admin/components/ui/select";
16
+ import type { AdminDocument } from "@admin/core/api/admin-client";
17
+ import { getAdminRuntime } from "@admin/core/runtime/admin-runtime.svelte";
18
+ import CollectionListFilterPopover from "@admin/features/collections/collection-list-filter-popover.svelte";
19
+ import type { CollectionListPreset } from "@admin/features/collections/collection-list-preferences-controller.svelte";
20
+ import {
21
+ listPageSizes,
22
+ type ListFilter,
23
+ type ListPageSize,
24
+ } from "@admin/features/collections/list-workspace";
25
+
26
+ const runtime = getAdminRuntime();
27
+
28
+ let {
29
+ slug,
30
+ query,
31
+ searchLabel,
32
+ searchInput = $bindable<HTMLInputElement | null>(null),
33
+ filterFields,
34
+ filters,
35
+ canReadField,
36
+ folders,
37
+ requestedFolder,
38
+ manageFoldersLink,
39
+ showFolder = false,
40
+ showHierarchy = false,
41
+ hierarchy = false,
42
+ sessionActive = false,
43
+ presets,
44
+ presetsPending = false,
45
+ statusColumnAvailable,
46
+ availableColumnFields,
47
+ visibleColumns,
48
+ showStatus,
49
+ showID,
50
+ showCreated,
51
+ showUpdated,
52
+ pageSize,
53
+ onSearch,
54
+ onClearSearch,
55
+ onFiltersChange,
56
+ onSelectFolder,
57
+ onSelectView,
58
+ onSavePreset,
59
+ onApplyPreset,
60
+ onDeletePreset,
61
+ onToggleColumn,
62
+ onToggleStatus,
63
+ onToggleID,
64
+ onToggleCreated,
65
+ onToggleUpdated,
66
+ onSelectPageSize,
67
+ }: {
68
+ slug: string;
69
+ query: string;
70
+ searchLabel: string;
71
+ searchInput?: HTMLInputElement | null;
72
+ filterFields: readonly SchemaField[];
73
+ filters: readonly ListFilter[];
74
+ canReadField: (path: string) => boolean;
75
+ folders: readonly AdminDocument[];
76
+ requestedFolder: string;
77
+ manageFoldersLink?: Snippet;
78
+ showFolder?: boolean;
79
+ showHierarchy?: boolean;
80
+ hierarchy?: boolean;
81
+ sessionActive?: boolean;
82
+ presets: readonly CollectionListPreset[];
83
+ presetsPending?: boolean;
84
+ statusColumnAvailable: boolean;
85
+ availableColumnFields: readonly SchemaField[];
86
+ visibleColumns: readonly SchemaField[];
87
+ showStatus: boolean;
88
+ showID: boolean;
89
+ showCreated: boolean;
90
+ showUpdated: boolean;
91
+ pageSize: ListPageSize;
92
+ onSearch: (value: string) => void;
93
+ onClearSearch: () => void;
94
+ onFiltersChange: (filters: readonly ListFilter[]) => void;
95
+ onSelectFolder: (folder: string) => void;
96
+ onSelectView: (view: "list" | "hierarchy") => void;
97
+ onSavePreset: (name: string) => Promise<boolean>;
98
+ onApplyPreset: (preset: CollectionListPreset) => void;
99
+ onDeletePreset: (name: string) => void;
100
+ onToggleColumn: (path: string, show: boolean) => void;
101
+ onToggleStatus: (show: boolean) => void;
102
+ onToggleID: (show: boolean) => void;
103
+ onToggleCreated: (show: boolean) => void;
104
+ onToggleUpdated: (show: boolean) => void;
105
+ onSelectPageSize: (size: ListPageSize) => void;
106
+ } = $props();
107
+
108
+ let presetName = $state("");
109
+ const sortedPresets = $derived(
110
+ [...presets].sort((left, right) =>
111
+ left.name.localeCompare(right.name, runtime.i18n.language, {
112
+ numeric: true,
113
+ sensitivity: "base",
114
+ })
115
+ )
116
+ );
117
+ const sortedFolders = $derived(
118
+ [...folders].sort((left, right) =>
119
+ folderLabel(left).localeCompare(folderLabel(right), runtime.i18n.language, {
120
+ numeric: true,
121
+ sensitivity: "base",
122
+ })
123
+ )
124
+ );
125
+ const selectedFolderLabel = $derived(
126
+ requestedFolder === ""
127
+ ? runtime.i18n.t("collections:allFolders")
128
+ : String(
129
+ folders.find((folder) => folder.id === requestedFolder)?.name ??
130
+ folders.find((folder) => folder.id === requestedFolder)?.title ??
131
+ requestedFolder
132
+ )
133
+ );
134
+
135
+ function folderLabel(folder: AdminDocument) {
136
+ return String(folder.name ?? folder.title ?? folder.id);
137
+ }
138
+
139
+ async function savePreset() {
140
+ if (await onSavePreset(presetName)) presetName = "";
141
+ }
142
+ </script>
143
+
144
+ <div
145
+ class="mt-5 flex flex-wrap items-center gap-2 rounded-[3px] bg-control p-2"
146
+ data-slot="collection-list-workspace-toolbar"
147
+ >
148
+ <div class="relative min-w-52 flex-1 sm:max-w-[320px]">
149
+ <SearchIcon
150
+ class="pointer-events-none absolute top-1/2 start-3.5 size-3.5 -translate-y-1/2 text-foreground-faint"
151
+ aria-hidden="true"
152
+ />
153
+ <label class="sr-only" for="collection-search">{searchLabel}</label>
154
+ <Input
155
+ id="collection-search"
156
+ bind:ref={searchInput}
157
+ type="search"
158
+ class="h-[31px] border-control-border bg-background px-8 text-[12.5px]"
159
+ value={query}
160
+ oninput={(event) => onSearch(event.currentTarget.value)}
161
+ placeholder={runtime.i18n.t("collections:searchPlaceholder", { label: searchLabel })}
162
+ />
163
+ {#if query !== ""}
164
+ <Button
165
+ variant="ghost"
166
+ size="icon-xs"
167
+ class="absolute top-1/2 end-1.5 -translate-y-1/2"
168
+ onclick={onClearSearch}
169
+ aria-label={runtime.i18n.t("collections:clearSearch")}
170
+ >
171
+ <XIcon class="size-3" />
172
+ </Button>
173
+ {/if}
174
+ </div>
175
+
176
+ <div class="ms-auto flex flex-wrap items-center justify-end gap-2">
177
+ {#if filterFields.length > 0}
178
+ {#key slug}
179
+ <CollectionListFilterPopover
180
+ i18n={runtime.i18n}
181
+ fields={filterFields}
182
+ {filters}
183
+ {canReadField}
184
+ {onFiltersChange}
185
+ />
186
+ {/key}
187
+ {/if}
188
+
189
+ <Popover>
190
+ <PopoverTrigger
191
+ class={buttonVariants({ variant: "outline", class: "h-[31px] gap-1.75 px-3" })}
192
+ >
193
+ <Columns3Icon class="size-3 text-foreground-faint" />
194
+ {runtime.i18n.t("collections:columns")}
195
+ </PopoverTrigger>
196
+ <PopoverContent align="end" class="w-44 gap-1 p-2">
197
+ <p class="px-2 pt-1 pb-1 text-[11px] font-medium text-foreground-muted">
198
+ {runtime.i18n.t("collections:columns")}
199
+ </p>
200
+ {#if statusColumnAvailable}
201
+ <label
202
+ class="flex h-9 cursor-pointer items-center gap-2.5 rounded-[3px] px-2 text-[13px] text-foreground-muted hover:bg-control hover:text-foreground-strong"
203
+ >
204
+ <Checkbox
205
+ checked={showStatus}
206
+ onCheckedChange={onToggleStatus}
207
+ aria-label={runtime.i18n.t("collections:showStatusColumn")}
208
+ />
209
+ {runtime.i18n.t("collections:status")}
210
+ </label>
211
+ {/if}
212
+ {#each availableColumnFields as field (field.id)}
213
+ {#if canReadField(field.path)}
214
+ <label
215
+ class="flex h-9 cursor-pointer items-center gap-2.5 rounded-[3px] px-2 text-[13px] text-foreground-muted hover:bg-control hover:text-foreground-strong"
216
+ >
217
+ <Checkbox
218
+ checked={visibleColumns.some((candidate) => candidate.path === field.path)}
219
+ onCheckedChange={(checked) => onToggleColumn(field.path, checked)}
220
+ aria-label={runtime.i18n.t("collections:showColumn", {
221
+ label: field.admin.label,
222
+ })}
223
+ />
224
+ <span class="truncate">{field.admin.label}</span>
225
+ </label>
226
+ {/if}
227
+ {/each}
228
+ <label
229
+ class="flex h-9 cursor-pointer items-center gap-2.5 rounded-[3px] px-2 text-[13px] text-foreground-muted hover:bg-control hover:text-foreground-strong"
230
+ >
231
+ <Checkbox
232
+ checked={showID}
233
+ onCheckedChange={onToggleID}
234
+ aria-label={runtime.i18n.t("collections:showIDColumn")}
235
+ />
236
+ ID
237
+ </label>
238
+ <label
239
+ class="flex h-9 cursor-pointer items-center gap-2.5 rounded-[3px] px-2 text-[13px] text-foreground-muted hover:bg-control hover:text-foreground-strong"
240
+ >
241
+ <Checkbox
242
+ checked={showCreated}
243
+ onCheckedChange={onToggleCreated}
244
+ aria-label={runtime.i18n.t("collections:showCreatedColumn")}
245
+ />
246
+ {runtime.i18n.t("collections:created")}
247
+ </label>
248
+ <label
249
+ class="flex h-9 cursor-pointer items-center gap-2.5 rounded-[3px] px-2 text-[13px] text-foreground-muted hover:bg-control hover:text-foreground-strong"
250
+ >
251
+ <Checkbox
252
+ checked={showUpdated}
253
+ onCheckedChange={onToggleUpdated}
254
+ aria-label={runtime.i18n.t("collections:showUpdatedColumn")}
255
+ />
256
+ {runtime.i18n.t("collections:updated")}
257
+ </label>
258
+ </PopoverContent>
259
+ </Popover>
260
+
261
+ {#if sessionActive}
262
+ <Popover>
263
+ <PopoverTrigger
264
+ class={buttonVariants({ variant: "outline", class: "h-[31px] gap-1.75 px-3" })}
265
+ >
266
+ <SaveIcon class="size-3" />
267
+ {runtime.i18n.t("collections:savedViews")}
268
+ </PopoverTrigger>
269
+ <PopoverContent align="end" class="w-56 gap-1 p-2">
270
+ <form
271
+ class="flex gap-1"
272
+ onsubmit={(event) => {
273
+ event.preventDefault();
274
+ void savePreset();
275
+ }}
276
+ >
277
+ <Input
278
+ bind:value={presetName}
279
+ aria-label={runtime.i18n.t("collections:savedViewName")}
280
+ placeholder={runtime.i18n.t("collections:viewName")}
281
+ class="h-8 min-w-0"
282
+ />
283
+ <Button
284
+ type="submit"
285
+ size="sm"
286
+ class="h-8"
287
+ disabled={presetName.trim() === "" || presetsPending}
288
+ >
289
+ {runtime.i18n.t("general:save")}
290
+ </Button>
291
+ </form>
292
+ {#each sortedPresets as preset (preset.name)}
293
+ <div class="flex items-center gap-1">
294
+ <button
295
+ class="min-w-0 flex-1 truncate rounded-[3px] px-2 py-1.5 text-start text-[12.5px] hover:bg-control"
296
+ onclick={() => onApplyPreset(preset)}
297
+ >
298
+ {preset.name}
299
+ </button>
300
+ <Button
301
+ variant="ghost"
302
+ size="icon-xs"
303
+ aria-label={runtime.i18n.t("collections:deleteSavedView", {
304
+ name: preset.name,
305
+ })}
306
+ onclick={() => onDeletePreset(preset.name)}
307
+ >
308
+ <XIcon class="size-3" />
309
+ </Button>
310
+ </div>
311
+ {/each}
312
+ </PopoverContent>
313
+ </Popover>
314
+ {/if}
315
+
316
+ <Popover>
317
+ <PopoverTrigger
318
+ class={buttonVariants({ variant: "outline", class: "h-[31px] gap-1.75 px-3" })}
319
+ >
320
+ <SlidersHorizontalIcon class="size-3" />
321
+ {runtime.i18n.t("general:more")}
322
+ </PopoverTrigger>
323
+ <PopoverContent align="end" class="w-64 gap-4 p-3">
324
+ {#if showFolder}
325
+ <div class="grid gap-2">
326
+ <label class="text-[11px] font-medium text-foreground-muted" for="folder-filter">
327
+ {runtime.i18n.t("collections:folder")}
328
+ </label>
329
+ <Select type="single" value={requestedFolder} onValueChange={onSelectFolder}>
330
+ <SelectTrigger
331
+ id="folder-filter"
332
+ size="toolbar"
333
+ aria-label={runtime.i18n.t("collections:folder")}
334
+ >
335
+ {selectedFolderLabel}
336
+ </SelectTrigger>
337
+ <SelectContent>
338
+ <SelectItem value="" label={runtime.i18n.t("collections:allFolders")} />
339
+ {#each sortedFolders as folder (folder.id)}
340
+ <SelectItem value={folder.id} label={folderLabel(folder)} />
341
+ {/each}
342
+ </SelectContent>
343
+ </Select>
344
+ {#if manageFoldersLink !== undefined}{@render manageFoldersLink()}{/if}
345
+ </div>
346
+ {/if}
347
+ {#if showHierarchy}
348
+ <div class="grid gap-2">
349
+ <p class="text-[11px] font-medium text-foreground-muted">
350
+ {runtime.i18n.t("collections:view")}
351
+ </p>
352
+ <div
353
+ class="flex rounded-[3px] border border-control-border bg-background p-0.5"
354
+ aria-label={runtime.i18n.t("collections:collectionView")}
355
+ >
356
+ <Button
357
+ variant="ghost"
358
+ size="sm"
359
+ class="h-7 flex-1"
360
+ aria-pressed={!hierarchy}
361
+ onclick={() => onSelectView("list")}
362
+ >
363
+ {runtime.i18n.t("collections:listView")}
364
+ </Button>
365
+ <Button
366
+ variant="ghost"
367
+ size="sm"
368
+ class="h-7 flex-1"
369
+ aria-pressed={hierarchy}
370
+ onclick={() => onSelectView("hierarchy")}
371
+ >
372
+ <ListTreeIcon class="size-3" />
373
+ {runtime.i18n.t("collections:hierarchy")}
374
+ </Button>
375
+ </div>
376
+ </div>
377
+ {/if}
378
+ <div class="grid gap-2">
379
+ <label class="text-[11px] font-medium text-foreground-muted" for="page-size">
380
+ {runtime.i18n.t("collections:pageSize")}
381
+ </label>
382
+ <Select
383
+ type="single"
384
+ value={String(pageSize)}
385
+ onValueChange={(next) => onSelectPageSize(Number(next) as ListPageSize)}
386
+ >
387
+ <SelectTrigger
388
+ id="page-size"
389
+ size="toolbar"
390
+ aria-label={runtime.i18n.t("collections:documentsPerPage")}
391
+ >
392
+ {runtime.i18n.t("collections:perPage", {
393
+ count: runtime.i18n.formatNumber(pageSize),
394
+ })}
395
+ </SelectTrigger>
396
+ <SelectContent>
397
+ {#each listPageSizes as size}<SelectItem
398
+ value={String(size)}
399
+ label={runtime.i18n.t("collections:perPage", {
400
+ count: runtime.i18n.formatNumber(size),
401
+ })}
402
+ />{/each}
403
+ </SelectContent>
404
+ </Select>
405
+ </div>
406
+ </PopoverContent>
407
+ </Popover>
408
+ </div>
409
+ </div>
@@ -0,0 +1,44 @@
1
+ <script lang="ts">
2
+ import { Skeleton } from "@admin/components/ui/skeleton";
3
+ import { Table, TableBody, TableCell, TableRow } from "@admin/components/ui/table";
4
+ import { getAdminRuntime } from "@admin/core/runtime/admin-runtime.svelte";
5
+
6
+ const runtime = getAdminRuntime();
7
+
8
+ let {
9
+ showStatus,
10
+ showID,
11
+ showCreated,
12
+ showUpdated,
13
+ }: {
14
+ showStatus: boolean;
15
+ showID: boolean;
16
+ showCreated: boolean;
17
+ showUpdated: boolean;
18
+ } = $props();
19
+ const widths = ["w-[46%]", "w-[61%]", "w-[38%]", "w-[54%]", "w-[43%]"];
20
+ </script>
21
+
22
+ <Table
23
+ class="min-w-[640px] border-collapse text-start"
24
+ aria-label={runtime.i18n.t("collections:loadingDocuments")}
25
+ >
26
+ <TableBody>
27
+ {#each widths as width (width)}
28
+ <TableRow>
29
+ <TableCell class="w-7 px-2.5"><Skeleton class="size-4" /></TableCell>
30
+ <TableCell>
31
+ <Skeleton class="h-3.5 {width}" />
32
+ </TableCell>
33
+ {#if showStatus}
34
+ <TableCell class="w-36"><Skeleton class="h-3 w-20" /></TableCell>
35
+ {/if}
36
+ {#if showID}<TableCell><Skeleton class="h-2.5 w-20" /></TableCell>{/if}
37
+ {#if showCreated}<TableCell><Skeleton class="h-2.5 w-16" /></TableCell>{/if}
38
+ {#if showUpdated}
39
+ <TableCell class="w-28"><Skeleton class="ms-auto h-2.5 w-16" /></TableCell>
40
+ {/if}
41
+ </TableRow>
42
+ {/each}
43
+ </TableBody>
44
+ </Table>