@questpie/admin 3.5.1 → 3.5.3

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 (94) hide show
  1. package/dist/client/builder/types/collection-types.d.mts +9 -0
  2. package/dist/client/components/actions/action-dialog.mjs +5 -0
  3. package/dist/client/components/fields/rich-text-editor/bubble-menu.mjs +7 -0
  4. package/dist/client/components/fields/rich-text-editor/extensions.mjs +17 -1
  5. package/dist/client/components/fields/rich-text-editor/index.d.mts +2 -1
  6. package/dist/client/components/fields/rich-text-editor/index.mjs +35 -74
  7. package/dist/client/components/fields/rich-text-editor/slash-commands.mjs +30 -7
  8. package/dist/client/components/fields/rich-text-editor/toolbar.mjs +1 -312
  9. package/dist/client/components/fields/rich-text-editor/types.d.mts +4 -0
  10. package/dist/client/components/fields/rich-text-editor/types.mjs +1 -1
  11. package/dist/client/components/fields/rich-text-editor/utils.mjs +6 -12
  12. package/dist/client/components/filter-builder/filter-builder-sheet.mjs +75 -22
  13. package/dist/client/components/ui/dropdown-menu.mjs +1 -34
  14. package/dist/client/hooks/query-access.d.mts +9 -0
  15. package/dist/client/hooks/query-access.mjs +20 -0
  16. package/dist/client/hooks/typed-hooks.d.mts +4 -2
  17. package/dist/client/hooks/typed-hooks.mjs +30 -29
  18. package/dist/client/hooks/use-reactive-fields.d.mts +1 -0
  19. package/dist/client/hooks/use-reactive-fields.mjs +16 -1
  20. package/dist/client/hooks/use-server-actions.mjs +12 -1
  21. package/dist/client/hooks/use-view-state.mjs +15 -7
  22. package/dist/client/lib/view-filter-utils.mjs +30 -0
  23. package/dist/client/preview/block-scope-context.d.mts +2 -2
  24. package/dist/client/preview/preview-banner.d.mts +2 -2
  25. package/dist/client/preview/preview-field.d.mts +4 -4
  26. package/dist/client/scope/picker.d.mts +2 -2
  27. package/dist/client/scope/provider.d.mts +2 -2
  28. package/dist/client/styles/base.css +69 -77
  29. package/dist/client/utils/build-field-definitions-from-schema.mjs +1 -0
  30. package/dist/client/views/collection/auto-form-fields.mjs +3 -2
  31. package/dist/client/views/collection/cells/primitive-cells.mjs +9 -6
  32. package/dist/client/views/collection/columns/build-columns.mjs +3 -1
  33. package/dist/client/views/collection/field-renderer.mjs +11 -3
  34. package/dist/client/views/collection/form-view.mjs +207 -202
  35. package/dist/client/views/collection/list-view.mjs +581 -183
  36. package/dist/client/views/collection/outline.mjs +44 -19
  37. package/dist/client/views/collection/quick-filter-bar.mjs +45 -0
  38. package/dist/client/views/collection/table-view.mjs +60 -16
  39. package/dist/client/views/globals/global-form-view.mjs +12 -9
  40. package/dist/client/views/layout/admin-layout.mjs +1 -1
  41. package/dist/client/views/layout/admin-sidebar.mjs +20 -14
  42. package/dist/client/views/layout/admin-theme.mjs +5 -4
  43. package/dist/client.mjs +1 -1
  44. package/dist/components/rich-text/rich-text-renderer.d.mts +5 -5
  45. package/dist/components/rich-text/rich-text-renderer.mjs +5 -2
  46. package/dist/index.mjs +1 -1
  47. package/dist/modules/admin.d.mts +1 -1
  48. package/dist/server/augmentation/actions.d.mts +4 -3
  49. package/dist/server/augmentation/dashboard.d.mts +11 -11
  50. package/dist/server/augmentation/form-layout.d.mts +11 -6
  51. package/dist/server/augmentation/index.d.mts +7 -0
  52. package/dist/server/augmentation/sidebar.d.mts +8 -8
  53. package/dist/server/codegen/admin-client-template.mjs +55 -38
  54. package/dist/server/fields/index.d.mts +1 -1
  55. package/dist/server/fields/rich-text.d.mts +16 -17
  56. package/dist/server/fields/rich-text.mjs +18 -7
  57. package/dist/server/i18n/messages/cs.mjs +2 -0
  58. package/dist/server/i18n/messages/de.mjs +2 -0
  59. package/dist/server/i18n/messages/en.mjs +4 -0
  60. package/dist/server/i18n/messages/es.mjs +2 -0
  61. package/dist/server/i18n/messages/fr.mjs +2 -0
  62. package/dist/server/i18n/messages/pl.mjs +2 -0
  63. package/dist/server/i18n/messages/pt.mjs +2 -0
  64. package/dist/server/i18n/messages/sk.mjs +2 -0
  65. package/dist/server/modules/admin/block/block-builder.d.mts +0 -8
  66. package/dist/server/modules/admin/block/introspection.d.mts +2 -2
  67. package/dist/server/modules/admin/collections/account.d.mts +53 -52
  68. package/dist/server/modules/admin/collections/admin-locks.d.mts +57 -56
  69. package/dist/server/modules/admin/collections/admin-preferences.d.mts +3 -2
  70. package/dist/server/modules/admin/collections/admin-saved-views.d.mts +50 -49
  71. package/dist/server/modules/admin/collections/apikey.d.mts +72 -71
  72. package/dist/server/modules/admin/collections/assets.d.mts +42 -41
  73. package/dist/server/modules/admin/collections/session.d.mts +46 -45
  74. package/dist/server/modules/admin/collections/user.d.mts +67 -66
  75. package/dist/server/modules/admin/collections/verification.d.mts +39 -38
  76. package/dist/server/modules/admin/index.d.mts +3 -3
  77. package/dist/server/modules/admin/routes/admin-config.d.mts +2 -2
  78. package/dist/server/modules/admin/routes/admin-config.mjs +39 -23
  79. package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
  80. package/dist/server/modules/admin/routes/execute-action.mjs +28 -8
  81. package/dist/server/modules/admin/routes/locales.d.mts +2 -2
  82. package/dist/server/modules/admin/routes/reactive.mjs +2 -2
  83. package/dist/server/modules/admin/routes/route-helpers.d.mts +11 -7
  84. package/dist/server/modules/admin/routes/translations.d.mts +4 -4
  85. package/dist/server/modules/admin/routes/widget-data.mjs +12 -4
  86. package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +27 -27
  87. package/dist/server/modules/audit/.generated/module.d.mts +6 -6
  88. package/dist/server/modules/audit/collections/audit-log.d.mts +40 -39
  89. package/dist/server/plugin.mjs +3 -3
  90. package/dist/server.d.mts +1 -1
  91. package/dist/shared/types/index.d.mts +1 -0
  92. package/dist/shared/types/saved-views.types.d.mts +14 -7
  93. package/dist/shared.d.mts +3 -2
  94. package/package.json +4 -3
@@ -1,9 +1,5 @@
1
- import { useTranslation } from "../../../i18n/hooks.mjs";
2
1
  import { cn } from "../../../lib/utils.mjs";
3
2
  import { Button } from "../../ui/button.mjs";
4
- import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue } from "../../ui/select.mjs";
5
- import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuTrigger } from "../../ui/dropdown-menu.mjs";
6
- import { TableControls } from "./table-controls.mjs";
7
3
  import { Icon } from "@iconify/react";
8
4
  import "react";
9
5
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -43,36 +39,6 @@ const EDITOR_ICONS = {
43
39
  insert: "ph:plus",
44
40
  more: "ph:dots-three-vertical"
45
41
  };
46
- const HEADING_OPTIONS = [
47
- {
48
- value: "paragraph",
49
- group: "text"
50
- },
51
- {
52
- value: "1",
53
- group: "heading"
54
- },
55
- {
56
- value: "2",
57
- group: "heading"
58
- },
59
- {
60
- value: "3",
61
- group: "heading"
62
- },
63
- {
64
- value: "4",
65
- group: "heading"
66
- },
67
- {
68
- value: "5",
69
- group: "heading"
70
- },
71
- {
72
- value: "6",
73
- group: "heading"
74
- }
75
- ];
76
42
  const ALIGNMENT_OPTIONS = [
77
43
  {
78
44
  value: "left",
@@ -95,16 +61,6 @@ const ALIGNMENT_OPTIONS = [
95
61
  labelKey: "editor.alignJustify"
96
62
  }
97
63
  ];
98
- function getHeadingOptionLabel(value, t) {
99
- if (value === "paragraph") return t("editor.paragraph");
100
- return t("editor.heading", { level: value });
101
- }
102
- function getCurrentAlignment(editor) {
103
- if (editor.isActive({ textAlign: "center" })) return "center";
104
- if (editor.isActive({ textAlign: "right" })) return "right";
105
- if (editor.isActive({ textAlign: "justify" })) return "justify";
106
- return "left";
107
- }
108
64
  /**
109
65
  * Icon-based toolbar button with tooltip showing keyboard shortcuts
110
66
  */
@@ -132,273 +88,6 @@ function ToolbarButton({ active, disabled, title, onClick, icon: iconName, child
132
88
  }) : children
133
89
  });
134
90
  }
135
- /**
136
- * Toolbar button group with divider
137
- */
138
- function ToolbarGroup({ children }) {
139
- return /* @__PURE__ */ jsx("div", {
140
- className: "flex items-center gap-1",
141
- role: "group",
142
- children
143
- });
144
- }
145
- function ToolbarMenuItem({ active, disabled, icon, label, onClick, shortcut }) {
146
- return /* @__PURE__ */ jsxs(DropdownMenuItem, {
147
- "aria-current": active ? "true" : void 0,
148
- disabled,
149
- onClick,
150
- className: cn("min-w-44", active ? "bg-accent text-accent-foreground" : ""),
151
- children: [
152
- /* @__PURE__ */ jsx(Icon, {
153
- "aria-hidden": "true",
154
- icon
155
- }),
156
- /* @__PURE__ */ jsx("span", { children: label }),
157
- shortcut ? /* @__PURE__ */ jsx(DropdownMenuShortcut, { children: shortcut }) : active ? /* @__PURE__ */ jsx(Icon, {
158
- "aria-hidden": "true",
159
- icon: "ph:check",
160
- className: "ml-auto size-3.5"
161
- }) : null
162
- ]
163
- });
164
- }
165
- /**
166
- * Main toolbar component with icon-based buttons
167
- */
168
- function RichTextToolbar({ editor, features, disabled, headingValue, onHeadingChange, onLinkClick, onImageClick, onTableClick, inTable }) {
169
- const { t } = useTranslation();
170
- const isEditable = !disabled;
171
- const currentAlignment = getCurrentAlignment(editor);
172
- const currentAlignmentOption = ALIGNMENT_OPTIONS.find((option) => option.value === currentAlignment) ?? ALIGNMENT_OPTIONS[0];
173
- const hasMoreFormatting = features.underline || features.strike || features.code;
174
- const hasBlockMenu = features.bulletList || features.orderedList || features.blockquote || features.codeBlock || features.horizontalRule;
175
- const hasInsertMenu = features.link || features.image || features.table;
176
- return /* @__PURE__ */ jsxs("div", {
177
- "aria-label": t("editor.richTextToolbar"),
178
- className: "qp-rich-text-editor__toolbar border-border-subtle bg-surface-low flex flex-nowrap items-center gap-1.5 overflow-x-auto border-b p-1.5",
179
- role: "toolbar",
180
- children: [
181
- features.history && /* @__PURE__ */ jsxs(ToolbarGroup, { children: [/* @__PURE__ */ jsx(ToolbarButton, {
182
- icon: EDITOR_ICONS.undo,
183
- disabled: !isEditable || !editor.can().undo(),
184
- title: t("editor.undo"),
185
- shortcut: "⌘Z",
186
- onClick: () => editor.chain().focus().undo().run()
187
- }), /* @__PURE__ */ jsx(ToolbarButton, {
188
- icon: EDITOR_ICONS.redo,
189
- disabled: !isEditable || !editor.can().redo(),
190
- title: t("editor.redo"),
191
- shortcut: "⌘⇧Z",
192
- onClick: () => editor.chain().focus().redo().run()
193
- })] }),
194
- features.heading && /* @__PURE__ */ jsx(ToolbarGroup, { children: /* @__PURE__ */ jsxs(Select, {
195
- value: headingValue,
196
- disabled: !isEditable,
197
- onValueChange: (value) => {
198
- if (typeof value === "string") onHeadingChange(value);
199
- },
200
- children: [/* @__PURE__ */ jsx(SelectTrigger, {
201
- "aria-label": t("editor.blockType"),
202
- className: "h-8 min-w-[136px] px-2.5 text-sm",
203
- size: "sm",
204
- children: /* @__PURE__ */ jsx(SelectValue, { children: HEADING_OPTIONS.find((option) => option.value === headingValue)?.value ? getHeadingOptionLabel(HEADING_OPTIONS.find((option) => option.value === headingValue)?.value ?? "paragraph", t) : t("editor.paragraph") })
205
- }), /* @__PURE__ */ jsxs(SelectContent, {
206
- align: "start",
207
- className: "min-w-[136px]",
208
- children: [
209
- /* @__PURE__ */ jsxs(SelectGroup, { children: [/* @__PURE__ */ jsx(SelectLabel, { children: t("editor.textBlocks") }), /* @__PURE__ */ jsx(SelectItem, {
210
- value: "paragraph",
211
- children: t("editor.paragraph")
212
- })] }),
213
- /* @__PURE__ */ jsx(SelectSeparator, {}),
214
- /* @__PURE__ */ jsxs(SelectGroup, { children: [/* @__PURE__ */ jsx(SelectLabel, { children: t("editor.headings") }), HEADING_OPTIONS.filter((option) => option.group === "heading").map((option) => /* @__PURE__ */ jsx(SelectItem, {
215
- value: option.value,
216
- children: getHeadingOptionLabel(option.value, t)
217
- }, option.value))] })
218
- ]
219
- })]
220
- }) }),
221
- /* @__PURE__ */ jsxs(ToolbarGroup, { children: [
222
- features.bold && /* @__PURE__ */ jsx(ToolbarButton, {
223
- icon: EDITOR_ICONS.bold,
224
- active: editor.isActive("bold"),
225
- disabled: !isEditable,
226
- title: t("editor.bold"),
227
- shortcut: "⌘B",
228
- onClick: () => editor.chain().focus().toggleBold().run()
229
- }),
230
- features.italic && /* @__PURE__ */ jsx(ToolbarButton, {
231
- icon: EDITOR_ICONS.italic,
232
- active: editor.isActive("italic"),
233
- disabled: !isEditable,
234
- title: t("editor.italic"),
235
- shortcut: "⌘I",
236
- onClick: () => editor.chain().focus().toggleItalic().run()
237
- }),
238
- hasMoreFormatting && /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
239
- nativeButton: false,
240
- render: /* @__PURE__ */ jsx(ToolbarButton, {
241
- icon: EDITOR_ICONS.formatting,
242
- active: editor.isActive("underline") || editor.isActive("strike") || editor.isActive("code"),
243
- disabled: !isEditable,
244
- title: t("editor.moreFormatting")
245
- })
246
- }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
247
- align: "start",
248
- className: "w-56",
249
- children: [
250
- /* @__PURE__ */ jsx(DropdownMenuLabel, { children: t("editor.formatting") }),
251
- features.underline && /* @__PURE__ */ jsx(ToolbarMenuItem, {
252
- icon: EDITOR_ICONS.underline,
253
- active: editor.isActive("underline"),
254
- disabled: !isEditable,
255
- label: t("editor.underline"),
256
- shortcut: "⌘U",
257
- onClick: () => editor.chain().focus().toggleUnderline().run()
258
- }),
259
- features.strike && /* @__PURE__ */ jsx(ToolbarMenuItem, {
260
- icon: EDITOR_ICONS.strikethrough,
261
- active: editor.isActive("strike"),
262
- disabled: !isEditable,
263
- label: t("editor.strikethrough"),
264
- onClick: () => editor.chain().focus().toggleStrike().run()
265
- }),
266
- features.code && /* @__PURE__ */ jsx(ToolbarMenuItem, {
267
- icon: EDITOR_ICONS.code,
268
- active: editor.isActive("code"),
269
- disabled: !isEditable,
270
- label: t("editor.code"),
271
- shortcut: "⌘E",
272
- onClick: () => editor.chain().focus().toggleCode().run()
273
- })
274
- ]
275
- })] })
276
- ] }),
277
- hasBlockMenu && /* @__PURE__ */ jsx(ToolbarGroup, { children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
278
- nativeButton: false,
279
- render: /* @__PURE__ */ jsx(ToolbarButton, {
280
- icon: EDITOR_ICONS.blocks,
281
- active: editor.isActive("bulletList") || editor.isActive("orderedList") || editor.isActive("blockquote") || editor.isActive("codeBlock"),
282
- disabled: !isEditable,
283
- title: t("editor.blocks")
284
- })
285
- }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
286
- align: "start",
287
- className: "w-56",
288
- children: [
289
- /* @__PURE__ */ jsx(DropdownMenuLabel, { children: t("editor.list") }),
290
- features.bulletList && /* @__PURE__ */ jsx(ToolbarMenuItem, {
291
- icon: EDITOR_ICONS.bulletList,
292
- active: editor.isActive("bulletList"),
293
- disabled: !isEditable,
294
- label: t("editor.unorderedList"),
295
- onClick: () => editor.chain().focus().toggleBulletList().run()
296
- }),
297
- features.orderedList && /* @__PURE__ */ jsx(ToolbarMenuItem, {
298
- icon: EDITOR_ICONS.orderedList,
299
- active: editor.isActive("orderedList"),
300
- disabled: !isEditable,
301
- label: t("editor.orderedList"),
302
- onClick: () => editor.chain().focus().toggleOrderedList().run()
303
- }),
304
- (features.blockquote || features.codeBlock || features.horizontalRule) && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
305
- features.blockquote && /* @__PURE__ */ jsx(ToolbarMenuItem, {
306
- icon: EDITOR_ICONS.blockquote,
307
- active: editor.isActive("blockquote"),
308
- disabled: !isEditable,
309
- label: t("editor.quote"),
310
- onClick: () => editor.chain().focus().toggleBlockquote().run()
311
- }),
312
- features.codeBlock && /* @__PURE__ */ jsx(ToolbarMenuItem, {
313
- icon: EDITOR_ICONS.codeBlock,
314
- active: editor.isActive("codeBlock"),
315
- disabled: !isEditable,
316
- label: t("editor.codeBlock"),
317
- onClick: () => editor.chain().focus().toggleCodeBlock().run()
318
- }),
319
- features.horizontalRule && /* @__PURE__ */ jsx(ToolbarMenuItem, {
320
- icon: EDITOR_ICONS.horizontalRule,
321
- disabled: !isEditable,
322
- label: t("editor.horizontalRule"),
323
- onClick: () => editor.chain().focus().setHorizontalRule().run()
324
- })
325
- ]
326
- })] }) }),
327
- features.align && /* @__PURE__ */ jsx(ToolbarGroup, { children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
328
- nativeButton: false,
329
- render: /* @__PURE__ */ jsx(ToolbarButton, {
330
- icon: currentAlignmentOption.icon,
331
- active: currentAlignment !== "left",
332
- disabled: !isEditable,
333
- title: t("editor.alignment")
334
- })
335
- }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
336
- align: "start",
337
- className: "w-56",
338
- children: [/* @__PURE__ */ jsx(DropdownMenuLabel, { children: t("editor.alignment") }), /* @__PURE__ */ jsx(DropdownMenuRadioGroup, {
339
- value: currentAlignment,
340
- onValueChange: (value) => {
341
- editor.chain().focus().setTextAlign(value).run();
342
- },
343
- children: ALIGNMENT_OPTIONS.map((option) => /* @__PURE__ */ jsxs(DropdownMenuRadioItem, {
344
- value: option.value,
345
- disabled: !isEditable,
346
- children: [/* @__PURE__ */ jsx(Icon, {
347
- "aria-hidden": "true",
348
- icon: option.icon
349
- }), /* @__PURE__ */ jsx("span", { children: t(option.labelKey) })]
350
- }, option.value))
351
- })]
352
- })] }) }),
353
- hasInsertMenu && /* @__PURE__ */ jsxs(ToolbarGroup, { children: [/* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
354
- nativeButton: false,
355
- render: /* @__PURE__ */ jsx(ToolbarButton, {
356
- icon: EDITOR_ICONS.insert,
357
- active: editor.isActive("link"),
358
- disabled: !isEditable,
359
- title: t("editor.insert")
360
- })
361
- }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
362
- align: "start",
363
- className: "w-56",
364
- children: [
365
- /* @__PURE__ */ jsx(DropdownMenuLabel, { children: t("editor.insert") }),
366
- features.link && /* @__PURE__ */ jsx(ToolbarMenuItem, {
367
- icon: EDITOR_ICONS.link,
368
- active: editor.isActive("link"),
369
- disabled: !isEditable,
370
- label: t("editor.link"),
371
- shortcut: "⌘K",
372
- onClick: onLinkClick
373
- }),
374
- features.image && /* @__PURE__ */ jsx(ToolbarMenuItem, {
375
- icon: EDITOR_ICONS.image,
376
- disabled: !isEditable,
377
- label: t("editor.image"),
378
- onClick: onImageClick
379
- }),
380
- features.table && /* @__PURE__ */ jsx(ToolbarMenuItem, {
381
- icon: EDITOR_ICONS.table,
382
- active: inTable,
383
- disabled: !isEditable,
384
- label: t("editor.table"),
385
- onClick: onTableClick
386
- })
387
- ]
388
- })] }), features.table && features.tableControls && inTable && /* @__PURE__ */ jsx(TableControls, {
389
- editor,
390
- disabled: !isEditable,
391
- inTable,
392
- triggerButton: /* @__PURE__ */ jsx(ToolbarButton, {
393
- icon: EDITOR_ICONS.table,
394
- active: inTable,
395
- disabled: !isEditable,
396
- title: t("editor.table")
397
- })
398
- })] })
399
- ]
400
- });
401
- }
402
91
 
403
92
  //#endregion
404
- export { EDITOR_ICONS, RichTextToolbar, ToolbarButton };
93
+ export { EDITOR_ICONS, ToolbarButton };
@@ -72,6 +72,10 @@ interface RichTextEditorProps extends FieldComponentProps<any> {
72
72
  * Enable media library picker for images
73
73
  */
74
74
  enableMediaLibrary?: boolean;
75
+ /**
76
+ * Output mode: "json" (default TipTap JSON) or "markdown" (plain markdown string)
77
+ */
78
+ outputMode?: "json" | "markdown";
75
79
  }
76
80
  //#endregion
77
81
  export { RichTextEditorProps };
@@ -3,7 +3,7 @@
3
3
  * Default feature configuration (all enabled)
4
4
  */
5
5
  const defaultFeatures = {
6
- toolbar: true,
6
+ toolbar: false,
7
7
  bubbleMenu: true,
8
8
  slashCommands: true,
9
9
  history: true,
@@ -1,16 +1,9 @@
1
1
  //#region src/client/components/fields/rich-text-editor/utils.ts
2
2
  /**
3
- * Get the current heading level or "paragraph"
3
+ * Get editor output in the configured format.
4
4
  */
5
- function getHeadingLevel(editor) {
6
- if (!editor) return "paragraph";
7
- for (let level = 1; level <= 6; level += 1) if (editor.isActive("heading", { level })) return String(level);
8
- return "paragraph";
9
- }
10
- /**
11
- * Get output as TipTap JSON.
12
- */
13
- function getOutput(editor) {
5
+ function getOutput(editor, mode = "json") {
6
+ if (mode === "markdown") return editor.storage.markdown?.getMarkdown?.() ?? "";
14
7
  return editor.getJSON();
15
8
  }
16
9
  /**
@@ -18,7 +11,8 @@ function getOutput(editor) {
18
11
  */
19
12
  function isSameValue(a, b) {
20
13
  if (a === b) return true;
21
- if (!a || !b) return false;
14
+ if (a == null || b == null) return false;
15
+ if (typeof a === "string" || typeof b === "string") return a === b;
22
16
  try {
23
17
  return JSON.stringify(a) === JSON.stringify(b);
24
18
  } catch {
@@ -47,4 +41,4 @@ function getCharacterCount(editor) {
47
41
  }
48
42
 
49
43
  //#endregion
50
- export { getCharacterCount, getHeadingLevel, getOutput, isSameValue };
44
+ export { getCharacterCount, getOutput, isSameValue };
@@ -4,6 +4,7 @@ import { Sheet, SheetContent, SheetFooter, SheetHeader, SheetTitle } from "../ui
4
4
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs.mjs";
5
5
  import { Switch } from "../ui/switch.mjs";
6
6
  import { SelectSingle } from "../primitives/select-single.mjs";
7
+ import { cloneFilters, filtersEqual, sortConfigEqual } from "../../lib/view-filter-utils.mjs";
7
8
  import { ColumnsTab } from "./columns-tab.mjs";
8
9
  import { FiltersTab } from "./filters-tab.mjs";
9
10
  import { SavedViewsTab } from "./saved-views-tab.mjs";
@@ -13,22 +14,10 @@ import { jsx, jsxs } from "react/jsx-runtime";
13
14
 
14
15
  //#region src/client/components/filter-builder/filter-builder-sheet.tsx
15
16
  const NO_GROUPING_VALUE = "__none";
17
+ const NO_SORT_VALUE = "__none";
16
18
  function arraysEqual(a, b, eq) {
17
19
  return a.length === b.length && a.every((v, i) => eq(v, b[i]));
18
20
  }
19
- function filterValueEqual(a, b) {
20
- if (a === b) return true;
21
- if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((v, i) => v === b[i]);
22
- return false;
23
- }
24
- function filtersEqual(a, b) {
25
- return arraysEqual(a, b, (x, y) => x.id === y.id && x.field === y.field && x.operator === y.operator && filterValueEqual(x.value, y.value));
26
- }
27
- function sortConfigEqual(a, b) {
28
- if (a === b) return true;
29
- if (!a || !b) return false;
30
- return a.field === b.field && a.direction === b.direction;
31
- }
32
21
  function viewConfigEqual(a, b) {
33
22
  return filtersEqual(a.filters, b.filters) && sortConfigEqual(a.sortConfig, b.sortConfig) && arraysEqual(a.visibleColumns, b.visibleColumns, (x, y) => x === y) && a.groupBy === b.groupBy && arraysEqual(a.collapsedGroups ?? [], b.collapsedGroups ?? [], (x, y) => x === y) && a.realtime === b.realtime && a.includeDeleted === b.includeDeleted;
34
23
  }
@@ -67,9 +56,10 @@ function ViewOptionRow({ title, description, control }) {
67
56
  })]
68
57
  });
69
58
  }
70
- function FilterBuilderSheet({ collection, availableFields, currentConfig, onConfigChange, isOpen, onOpenChange, defaultColumns, savedViews, savedViewsLoading = false, onSaveView, onDeleteView, supportsSoftDelete = false, groupableFields = EMPTY_GROUPABLE_FIELDS, defaultGroupBy = null }) {
59
+ function FilterBuilderSheet({ collection, availableFields, currentConfig, onConfigChange, isOpen, onOpenChange, defaultColumns, savedViews, savedViewsLoading = false, onSaveView, onDeleteView, supportsSoftDelete = false, groupableFields = EMPTY_GROUPABLE_FIELDS, defaultGroupBy = null, defaultFilters = [], panels }) {
71
60
  const resolvedSavedViews = savedViews ?? EMPTY_SAVED_VIEWS;
72
61
  const { t } = useTranslation();
62
+ const { columns: showColumns = true, filters: showFilters = true, savedViews: showSavedViews = true } = panels ?? {};
73
63
  const [localConfig, setLocalConfig] = useState(currentConfig);
74
64
  const [prev, setPrev] = useState({
75
65
  isOpen,
@@ -94,7 +84,7 @@ function FilterBuilderSheet({ collection, availableFields, currentConfig, onConf
94
84
  };
95
85
  const handleReset = () => {
96
86
  setLocalConfig({
97
- filters: [],
87
+ filters: cloneFilters(defaultFilters),
98
88
  sortConfig: null,
99
89
  visibleColumns: defaultColumns ?? availableFields.map((f) => f.name),
100
90
  groupBy: defaultGroupBy,
@@ -119,6 +109,21 @@ function FilterBuilderSheet({ collection, availableFields, currentConfig, onConf
119
109
  className: "size-4 opacity-60"
120
110
  })
121
111
  }))], [groupableFields, t]);
112
+ const sortOptions = useMemo(() => [{
113
+ value: NO_SORT_VALUE,
114
+ label: t("viewOptions.noSort"),
115
+ icon: /* @__PURE__ */ jsx(Icon, {
116
+ icon: "ph:sort-ascending",
117
+ className: "size-4 opacity-60"
118
+ })
119
+ }, ...availableFields.map((field) => ({
120
+ value: field.name,
121
+ label: field.label,
122
+ icon: /* @__PURE__ */ jsx(Icon, {
123
+ icon: "ph:text-aa",
124
+ className: "size-4 opacity-60"
125
+ })
126
+ }))], [availableFields, t]);
122
127
  return /* @__PURE__ */ jsx(Sheet, {
123
128
  open: isOpen,
124
129
  onOpenChange,
@@ -182,21 +187,69 @@ function FilterBuilderSheet({ collection, availableFields, currentConfig, onConf
182
187
  drawerTitle: t("viewOptions.groupBy"),
183
188
  className: "h-9 w-48"
184
189
  })
190
+ }),
191
+ /* @__PURE__ */ jsx(ViewOptionRow, {
192
+ title: t("viewOptions.sort"),
193
+ description: t("viewOptions.sortDescription"),
194
+ control: /* @__PURE__ */ jsxs("div", {
195
+ className: "flex items-center gap-2",
196
+ children: [/* @__PURE__ */ jsx(SelectSingle, {
197
+ id: "view-options-sort",
198
+ value: localConfig.sortConfig?.field ?? NO_SORT_VALUE,
199
+ onChange: (value) => {
200
+ const nextSortField = !value || value === NO_SORT_VALUE ? null : value;
201
+ setLocalConfig((prevConfig) => ({
202
+ ...prevConfig,
203
+ sortConfig: nextSortField ? {
204
+ field: nextSortField,
205
+ direction: prevConfig.sortConfig?.direction ?? "asc"
206
+ } : null,
207
+ pagination: {
208
+ ...prevConfig.pagination ?? { pageSize: 25 },
209
+ page: 1
210
+ }
211
+ }));
212
+ },
213
+ options: sortOptions,
214
+ clearable: false,
215
+ emptyMessage: t("viewOptions.noFieldsAvailable"),
216
+ placeholder: t("viewOptions.noSort"),
217
+ drawerTitle: t("viewOptions.sort"),
218
+ className: "h-9 w-48"
219
+ }), /* @__PURE__ */ jsx(Button, {
220
+ variant: "outline",
221
+ size: "icon-sm",
222
+ disabled: !localConfig.sortConfig?.field,
223
+ "aria-label": localConfig.sortConfig?.direction === "asc" ? t("table.sortDesc") : t("table.sortAsc"),
224
+ onClick: () => setLocalConfig((prevConfig) => ({
225
+ ...prevConfig,
226
+ sortConfig: prevConfig.sortConfig ? {
227
+ ...prevConfig.sortConfig,
228
+ direction: prevConfig.sortConfig.direction === "asc" ? "desc" : "asc"
229
+ } : null,
230
+ pagination: {
231
+ ...prevConfig.pagination ?? { pageSize: 25 },
232
+ page: 1
233
+ }
234
+ })),
235
+ children: /* @__PURE__ */ jsx(Icon, { icon: localConfig.sortConfig?.direction === "asc" ? "ph:sort-ascending" : "ph:sort-descending" })
236
+ })]
237
+ })
185
238
  })
186
239
  ]
187
240
  }), /* @__PURE__ */ jsxs(Tabs, {
188
- defaultValue: "columns",
241
+ defaultValue: showColumns ? "columns" : "filters",
189
242
  className: "mt-4",
190
243
  children: [
191
244
  /* @__PURE__ */ jsxs(TabsList, {
192
245
  className: "w-full",
193
246
  children: [
194
- /* @__PURE__ */ jsx(TabsTrigger, {
247
+ showColumns && /* @__PURE__ */ jsx(TabsTrigger, {
195
248
  value: "columns",
196
249
  className: "flex-1",
197
250
  children: t("viewOptions.columns")
198
251
  }),
199
- /* @__PURE__ */ jsxs(TabsTrigger, {
252
+ showFilters && /* @__PURE__ */ jsxs(TabsTrigger, {
200
253
  value: "filters",
201
254
  className: "flex-1",
202
255
  children: [t("viewOptions.filters"), localConfig.filters.length > 0 && /* @__PURE__ */ jsx("span", {
@@ -204,14 +257,14 @@ function FilterBuilderSheet({ collection, availableFields, currentConfig, onConf
204
257
  children: localConfig.filters.length
205
258
  })]
206
259
  }),
207
- /* @__PURE__ */ jsx(TabsTrigger, {
260
+ showSavedViews && /* @__PURE__ */ jsx(TabsTrigger, {
208
261
  value: "views",
209
262
  className: "flex-1",
210
263
  children: t("viewOptions.savedViews")
211
264
  })
212
265
  ]
213
266
  }),
214
- /* @__PURE__ */ jsx(TabsContent, {
267
+ showColumns && /* @__PURE__ */ jsx(TabsContent, {
215
268
  value: "columns",
216
269
  children: /* @__PURE__ */ jsx(ColumnsTab, {
217
270
  fields: availableFields,
@@ -222,7 +275,7 @@ function FilterBuilderSheet({ collection, availableFields, currentConfig, onConf
222
275
  }))
223
276
  })
224
277
  }),
225
- /* @__PURE__ */ jsx(TabsContent, {
278
+ showFilters && /* @__PURE__ */ jsx(TabsContent, {
226
279
  value: "filters",
227
280
  children: /* @__PURE__ */ jsx(FiltersTab, {
228
281
  fields: availableFields,
@@ -233,7 +286,7 @@ function FilterBuilderSheet({ collection, availableFields, currentConfig, onConf
233
286
  }))
234
287
  })
235
288
  }),
236
- /* @__PURE__ */ jsx(TabsContent, {
289
+ showSavedViews && /* @__PURE__ */ jsx(TabsContent, {
237
290
  value: "views",
238
291
  children: /* @__PURE__ */ jsx(SavedViewsTab, {
239
292
  collection,
@@ -33,14 +33,6 @@ function DropdownMenuContent({ align = "start", alignOffset = 0, side = "bottom"
33
33
  })
34
34
  }) });
35
35
  }
36
- function DropdownMenuLabel({ className, inset, ...props }) {
37
- return /* @__PURE__ */ jsx("div", {
38
- "data-slot": "dropdown-menu-label",
39
- "data-inset": inset,
40
- className: cn("qa-dropdown-menu__label text-muted-foreground font-chrome chrome-meta px-2.5 py-1.5 text-xs font-medium data-[inset]:pl-8", className),
41
- ...props
42
- });
43
- }
44
36
  function DropdownMenuItem({ className, inset, variant = "default", ...props }) {
45
37
  return /* @__PURE__ */ jsx(Menu.Item, {
46
38
  "data-slot": "dropdown-menu-item",
@@ -79,24 +71,6 @@ function DropdownMenuSubContent({ align = "start", alignOffset = -3, side = "rig
79
71
  ...props
80
72
  });
81
73
  }
82
- function DropdownMenuRadioGroup({ ...props }) {
83
- return /* @__PURE__ */ jsx(Menu.RadioGroup, {
84
- "data-slot": "dropdown-menu-radio-group",
85
- ...props
86
- });
87
- }
88
- function DropdownMenuRadioItem({ className, children, ...props }) {
89
- return /* @__PURE__ */ jsxs(Menu.RadioItem, {
90
- "data-slot": "dropdown-menu-radio-item",
91
- className: cn("qa-dropdown-menu__radio-item item-surface focus:border-border focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground relative flex min-h-9 cursor-default items-center gap-2.5 px-3 py-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5", className),
92
- ...props,
93
- children: [/* @__PURE__ */ jsx("span", {
94
- className: "pointer-events-none absolute right-3 flex items-center justify-center",
95
- "data-slot": "dropdown-menu-radio-item-indicator",
96
- children: /* @__PURE__ */ jsx(Menu.RadioItemIndicator, { children: /* @__PURE__ */ jsx(Icon, { icon: "ph:check" }) })
97
- }), children]
98
- });
99
- }
100
74
  function DropdownMenuSeparator({ className, ...props }) {
101
75
  return /* @__PURE__ */ jsx(Menu.Separator, {
102
76
  "data-slot": "dropdown-menu-separator",
@@ -104,13 +78,6 @@ function DropdownMenuSeparator({ className, ...props }) {
104
78
  ...props
105
79
  });
106
80
  }
107
- function DropdownMenuShortcut({ className, ...props }) {
108
- return /* @__PURE__ */ jsx("span", {
109
- "data-slot": "dropdown-menu-shortcut",
110
- className: cn("qa-dropdown-menu__shortcut text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground font-chrome chrome-meta ml-auto text-[0.625rem]", className),
111
- ...props
112
- });
113
- }
114
81
 
115
82
  //#endregion
116
- export { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger };
83
+ export { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger };
@@ -0,0 +1,9 @@
1
+ import "../builder/registry.mjs";
2
+ import { QuestpieQueryOptionsProxy } from "@questpie/tanstack-query";
3
+ import { QuestpieApp, QuestpieClient } from "questpie/client";
4
+
5
+ //#region src/client/hooks/query-access.d.ts
6
+
7
+ type CollectionQueryKey<TApp extends QuestpieApp> = keyof QuestpieQueryOptionsProxy<TApp>["collections"] & string;
8
+ //#endregion
9
+ export { CollectionQueryKey };
@@ -0,0 +1,20 @@
1
+ import { selectClient, useAdminStore } from "../runtime/provider.mjs";
2
+
3
+ //#region src/client/hooks/query-access.ts
4
+ /** Single intentional cast site for dynamic collection names in views. */
5
+ function adminCollectionKey(name) {
6
+ return name;
7
+ }
8
+ function getCollectionQueryApi(queryOpts, collection) {
9
+ return queryOpts.collections[collection];
10
+ }
11
+ function getGlobalQueryApi(queryOpts, globalName) {
12
+ return queryOpts.globals[globalName];
13
+ }
14
+ /** Typed app client from admin store (factory hooks with explicit TApp). */
15
+ function useAppClient() {
16
+ return useAdminStore(selectClient);
17
+ }
18
+
19
+ //#endregion
20
+ export { adminCollectionKey, getCollectionQueryApi, getGlobalQueryApi, useAppClient };