rimecms 0.27.0 → 0.27.2

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 (50) hide show
  1. package/README.md +26 -24
  2. package/dist/core/collections/auth/fields.d.ts +40 -40
  3. package/dist/core/fields/builders/select.js +1 -0
  4. package/dist/core/i18n/en/fields.d.ts +1 -0
  5. package/dist/core/i18n/en/fields.js +2 -1
  6. package/dist/core/i18n/fr/fields.d.ts +1 -0
  7. package/dist/core/i18n/fr/fields.js +2 -1
  8. package/dist/core/operations/hooks/before-read/set-document-thumbnail.server.js +2 -2
  9. package/dist/core/operations/hooks/before-read/sort-document-props.server.js +3 -1
  10. package/dist/fields/relation/component/Relation.svelte +16 -9
  11. package/dist/fields/relation/component/Relation.svelte.d.ts +2 -1
  12. package/dist/fields/relation/index.d.ts +1 -1
  13. package/dist/fields/rich-text/client.d.ts +9 -16
  14. package/dist/fields/rich-text/client.js +1 -1
  15. package/dist/fields/rich-text/component/RichText.svelte +1 -1
  16. package/dist/fields/rich-text/core/{config-builders.js → build-editor-config.js} +4 -0
  17. package/dist/fields/rich-text/core/features/fields/extension.js +2 -2
  18. package/dist/fields/rich-text/core/features/fields/index.d.ts +1 -1
  19. package/dist/fields/rich-text/core/features/fields/index.js +4 -2
  20. package/dist/fields/rich-text/core/features/link/component/link-selector.svelte +7 -7
  21. package/dist/fields/rich-text/core/features/link/index.d.ts +1 -4
  22. package/dist/fields/rich-text/core/features/resource/index.js +17 -11
  23. package/dist/fields/rich-text/core/features/resource/resource-extension.d.ts +2 -2
  24. package/dist/fields/rich-text/core/features/resource/resource-extension.js +5 -3
  25. package/dist/fields/rich-text/core/features/resource/resource.svelte +17 -19
  26. package/dist/fields/rich-text/core/features/upload/index.d.ts +2 -5
  27. package/dist/fields/rich-text/core/features/upload/index.js +15 -11
  28. package/dist/fields/rich-text/core/features/upload/upload-extension.d.ts +1 -2
  29. package/dist/fields/rich-text/core/features/upload/upload-extension.js +4 -1
  30. package/dist/fields/rich-text/core/features/upload/upload.svelte +13 -8
  31. package/dist/fields/types.d.ts +3 -0
  32. package/dist/panel/components/Root.svelte +2 -2
  33. package/dist/panel/components/fields/FieldsPreviewTrigger.svelte +1 -1
  34. package/dist/panel/components/fields/index.d.ts +2 -2
  35. package/dist/panel/components/sections/collection/upload-thumb-cell/UploadThumbCell.svelte +3 -2
  36. package/dist/panel/components/sections/document/upload-header/UploadHeader.svelte +2 -7
  37. package/dist/panel/components/sections/live/SidePanel.svelte +2 -2
  38. package/dist/panel/components/ui/breadcrumb/BreadCrumb.svelte +12 -6
  39. package/dist/panel/components/ui/card-document/card-document.svelte +2 -2
  40. package/dist/panel/components/ui/command/command-dialog.svelte +3 -1
  41. package/dist/panel/components/ui/command/command-dialog.svelte.d.ts +1 -0
  42. package/dist/panel/context/api-proxy.svelte.d.ts +2 -0
  43. package/dist/panel/context/api-proxy.svelte.js +9 -1
  44. package/dist/panel/context/documentForm.svelte.js +5 -4
  45. package/dist/panel/context/title.d.ts +7 -0
  46. package/dist/panel/context/title.js +9 -0
  47. package/dist/panel/pages/collection/Collection.svelte +2 -2
  48. package/dist/types.d.ts +1 -1
  49. package/package.json +4 -4
  50. /package/dist/fields/rich-text/core/{config-builders.d.ts → build-editor-config.d.ts} +0 -0
@@ -1,14 +1,20 @@
1
1
  import { Images } from '@lucide/svelte';
2
2
  import { Resource } from './resource-extension.js';
3
- const resourceFeatureNode = {
4
- label: 'Resource',
5
- icon: Images,
6
- isActive: ({ editor }) => editor.isActive('richt-text-resource'),
7
- suggestion: {
8
- command: ({ editor }) => editor.chain().focus().insertResource().run()
9
- }
3
+ export const ResourceFeature = (args) => {
4
+ const slug = args.source.split('?')[0];
5
+ return {
6
+ extension: Resource.configure(args).extend({
7
+ name: 'richt-text-resource-' + slug
8
+ }),
9
+ nodes: [
10
+ {
11
+ label: args.label || slug,
12
+ icon: Images,
13
+ isActive: ({ editor }) => editor.isActive('richt-text-resource-' + slug),
14
+ suggestion: {
15
+ command: ({ editor }) => editor.chain().focus().insertResource().run()
16
+ }
17
+ }
18
+ ]
19
+ };
10
20
  };
11
- export const ResourceFeature = (args) => ({
12
- extension: Resource.configure(args),
13
- nodes: [resourceFeatureNode]
14
- });
@@ -9,7 +9,7 @@ declare module '@tiptap/core' {
9
9
  }
10
10
  }
11
11
  export interface ResourceFeatureExtensionOptions {
12
- query?: string;
13
- slug: PrototypeSlug;
12
+ label?: string;
13
+ source: `${PrototypeSlug}${string}`;
14
14
  }
15
15
  export declare const Resource: Node<ResourceFeatureExtensionOptions, any>;
@@ -11,7 +11,9 @@ export const Resource = Node.create({
11
11
  return ['id', 'title', '_type', '_thumbnail'].reduce((acc, key) => {
12
12
  acc[key] = { default: null };
13
13
  return acc;
14
- }, {});
14
+ },
15
+ // Fresh attributes allowing to show the dialog immediately after insertion
16
+ { _fresh: { default: true } });
15
17
  },
16
18
  addCommands() {
17
19
  return {
@@ -24,10 +26,10 @@ export const Resource = Node.create({
24
26
  };
25
27
  },
26
28
  parseHTML() {
27
- return [{ tag: 'richt-text-resource' }];
29
+ return [{ tag: this.name }];
28
30
  },
29
31
  renderHTML({ HTMLAttributes }) {
30
- return ['richt-text-resource', mergeAttributes(HTMLAttributes)];
32
+ return [this.name, mergeAttributes(HTMLAttributes)];
31
33
  },
32
34
  addNodeView() {
33
35
  return SvelteNodeViewRenderer(ResourceComponent);
@@ -35,14 +35,16 @@
35
35
  } else {
36
36
  selected = null;
37
37
  }
38
+ if (node.attrs._fresh) {
39
+ // If the resource is fresh, we want to open the dialog to select a resource
40
+ isDialogOpen = true;
41
+ }
38
42
  });
39
43
 
40
44
  const APIProxy = getAPIProxyContext();
41
45
 
42
46
  // svelte-ignore state_referenced_locally
43
- const url = extension.options.query
44
- ? apiUrl(extension.options.slug, `?${extension.options.query}`)
45
- : apiUrl(extension.options.slug);
47
+ const url = apiUrl(extension.options.source);
46
48
 
47
49
  const ressource = APIProxy.getRessource<{ docs: GenericDoc[] }>(url);
48
50
  let docs = $state<GenericDoc[]>([]);
@@ -50,7 +52,7 @@
50
52
  $effect(() => {
51
53
  if (ressource.data) {
52
54
  docs = ressource.data.docs;
53
- // Update atttributes if document title has changed
55
+ // Update atttributes if document has changed
54
56
  if (node.attrs.id) {
55
57
  selected = docs.find((doc) => doc.id === node.attrs.id);
56
58
  }
@@ -58,27 +60,22 @@
58
60
  });
59
61
 
60
62
  // Handle dialog selection
61
- function handleResourceSelection(doc: GenericDoc) {
62
- // Close the dialog
63
+ function handleResourceSelection(doc: RequiredNodeAttributes) {
63
64
  isDialogOpen = false;
64
-
65
- // Update the selected resource
66
65
  selected = doc;
67
-
68
- // Update node attributes
69
66
  updateNodeAttributes();
70
67
  }
71
68
 
72
69
  // Handle removing resource
73
70
  function removeResource() {
74
71
  selected = null;
75
-
76
72
  // Update node attributes with empty values
77
73
  updateAttributes({
78
74
  id: null,
79
75
  title: null,
80
- slug: null,
81
- _thumbnail: null
76
+ _type: null,
77
+ _thumbnail: null,
78
+ _fresh: false
82
79
  });
83
80
  }
84
81
 
@@ -89,8 +86,9 @@
89
86
  updateAttributes({
90
87
  id: selected.id,
91
88
  title: selected.title,
92
- _type: extension.options.slug,
93
- _thumbnail: selected._thumbnail
89
+ _type: extension.options.source,
90
+ _thumbnail: selected._thumbnail,
91
+ _fresh: false
94
92
  });
95
93
  }
96
94
  </script>
@@ -102,16 +100,16 @@
102
100
  class:rz-richtext-resource--selected={!!selected}
103
101
  >
104
102
  {#if !selected}
105
- <Button class="rz-richtext-resource__add" variant="outline" size="sm" onclick={handleClick}
106
- >Add a resource</Button
107
- >
103
+ <Button class="rz-richtext-resource__add" variant="outline" size="sm" onclick={handleClick}>
104
+ Add a resource
105
+ </Button>
108
106
  {:else}
109
107
  <CardResource resource={selected as RequiredNodeAttributes} onCloseClick={removeResource} />
110
108
  {/if}
111
109
  </div>
112
110
  </NodeViewWrapper>
113
111
 
114
- <Command.Dialog bind:open={isDialogOpen}>
112
+ <Command.Dialog bind:open={isDialogOpen} preventScroll={false}>
115
113
  <Command.Input placeholder="Select a resource" />
116
114
 
117
115
  <Command.List>
@@ -1,6 +1,3 @@
1
- import type { CollectionSlug } from '../../../../../types.js';
2
1
  import type { RichTextFeature } from '../../types.js';
3
- export declare const UploadFeature: (args: {
4
- slug: CollectionSlug;
5
- query?: string;
6
- }) => RichTextFeature;
2
+ import { type UploadFeatureExtensionOptions } from './upload-extension.js';
3
+ export declare const UploadFeature: (args: UploadFeatureExtensionOptions) => RichTextFeature;
@@ -1,14 +1,18 @@
1
1
  import { Images } from '@lucide/svelte';
2
2
  import { Upload } from './upload-extension.js';
3
- const uploadFeatureNode = {
4
- label: 'Media',
5
- icon: Images,
6
- isActive: ({ editor }) => editor.isActive('richt-text-media'),
7
- suggestion: {
8
- command: ({ editor }) => editor.chain().focus().insertUpload().run()
9
- }
3
+ export const UploadFeature = (args) => {
4
+ const slug = args.source.split('?')[0];
5
+ return {
6
+ extension: Upload.configure(args).extend({ name: 'richt-text-upload-' + slug }),
7
+ nodes: [
8
+ {
9
+ label: 'Media',
10
+ icon: Images,
11
+ isActive: ({ editor }) => editor.isActive('richt-text-upload-' + slug),
12
+ suggestion: {
13
+ command: ({ editor }) => editor.chain().focus().insertUpload().run()
14
+ }
15
+ }
16
+ ]
17
+ };
10
18
  };
11
- export const UploadFeature = (args) => ({
12
- extension: Upload.configure(args),
13
- nodes: [uploadFeatureNode]
14
- });
@@ -2,8 +2,7 @@ import type { CollectionSlug } from '../../../../../types';
2
2
  import type { Dic } from '../../../../../util/types.js';
3
3
  import { Node } from '@tiptap/core';
4
4
  export interface UploadFeatureExtensionOptions {
5
- query?: string;
6
- slug: CollectionSlug;
5
+ source: `${CollectionSlug}${string}`;
7
6
  }
8
7
  declare module '@tiptap/core' {
9
8
  interface Commands<ReturnType> {
@@ -11,7 +11,10 @@ export const Upload = Node.create({
11
11
  return ['id', 'title', 'sizes', 'mimeType', 'url', 'filename', 'legend'].reduce((acc, key) => {
12
12
  acc[key] = { default: null };
13
13
  return acc;
14
- }, {});
14
+ },
15
+ // We use this attribute to determine if the resource is fresh or not.
16
+ // If it's fresh, we want to open the dialog
17
+ { _fresh: { default: true } });
15
18
  },
16
19
  addCommands() {
17
20
  return {
@@ -20,11 +20,12 @@
20
20
  url: string | null;
21
21
  filename: string | null;
22
22
  legend: string;
23
+ _fresh: boolean;
23
24
  };
24
25
 
25
26
  let { node, updateAttributes, extension }: NodeViewProps = $props();
26
27
  let isDialogOpen = $state(false);
27
- let selected = $state<Omit<NodeAttributes, 'legend'> | null>();
28
+ let selected = $state<Omit<NodeAttributes, 'legend' | '_fresh'> | null>();
28
29
  let legendElement = $state<HTMLParagraphElement>();
29
30
  let legend = $state('');
30
31
  let dialogLegendOpen = $state(false);
@@ -52,14 +53,16 @@
52
53
  } else {
53
54
  selected = null;
54
55
  }
56
+ if (node.attrs._fresh) {
57
+ // If the resource is fresh, we want to open the dialog to select a resource
58
+ isDialogOpen = true;
59
+ }
55
60
  });
56
61
 
57
62
  const APIProxy = getAPIProxyContext();
58
63
 
59
64
  // svelte-ignore state_referenced_locally
60
- const url = extension.options.query
61
- ? apiUrl(extension.options.slug, `?${extension.options.query}`)
62
- : apiUrl(extension.options.slug);
65
+ const url = apiUrl(extension.options.source);
63
66
 
64
67
  const ressource = APIProxy.getRessource<{ docs: UploadDoc[] }>(url);
65
68
  let docs = $state<UploadDoc[]>([]);
@@ -101,7 +104,8 @@
101
104
  sizes: null,
102
105
  mimeType: null,
103
106
  filename: null,
104
- legend: null
107
+ legend: null,
108
+ _fresh: false
105
109
  });
106
110
  }
107
111
 
@@ -116,7 +120,8 @@
116
120
  sizes: selected.sizes,
117
121
  mimeType: selected.mimeType,
118
122
  filename: selected.filename,
119
- legend
123
+ legend,
124
+ _fresh: false
120
125
  } as NodeAttributes);
121
126
  }
122
127
  </script>
@@ -167,7 +172,7 @@
167
172
  </div>
168
173
  {/snippet}
169
174
 
170
- {#snippet media(attributes: Omit<NodeAttributes, 'legend'>)}
175
+ {#snippet media(attributes: Omit<NodeAttributes, 'legend' | '_fresh'>)}
171
176
  <div class="rz-richtext-media__media">
172
177
  {#if attributes.mimeType?.includes('image')}
173
178
  <img src={attributes.url} alt="rich text" />
@@ -177,7 +182,7 @@
177
182
  </div>
178
183
  {/snippet}
179
184
 
180
- <Command.Dialog bind:open={isDialogOpen}>
185
+ <Command.Dialog bind:open={isDialogOpen} preventScroll={false}>
181
186
  <Command.Input placeholder="Select an image" />
182
187
 
183
188
  <Command.List>
@@ -78,16 +78,19 @@ export type FieldHookContext<T extends FormField = FormField> = {
78
78
  /** The field config */
79
79
  config: T;
80
80
  };
81
+ /** Field hook for client/server operations */
81
82
  export type FieldHookShared<T extends FormField = any> = (value: any, context: {
82
83
  config: T;
83
84
  data: Dic;
84
85
  }) => any;
86
+ /** Field hook for client-side operations */
85
87
  export type FieldHookClient = (value: any, context: {
86
88
  siblings: Record<string, any>;
87
89
  useField: DocumentFormContext['useField'];
88
90
  useBlocks: DocumentFormContext['useBlocks'];
89
91
  useTree: DocumentFormContext['useTree'];
90
92
  }) => any;
93
+ /** Field hook for server-side operations */
91
94
  export type FieldHook<T extends FormField = any> = (value: any, context: FieldHookContext<T>) => any;
92
95
  type FieldHooks = {
93
96
  beforeRead?: FieldHook[];
@@ -5,12 +5,12 @@
5
5
  import Nav from './ui/nav/Nav.svelte';
6
6
  import { Toaster } from './ui/sonner';
7
7
  import { setConfigContext } from '../context/config.svelte.js';
8
- import createContext from '../context/createContext.svelte.js';
9
8
  import { setLocaleContext } from '../context/locale.svelte.js';
10
9
  import { setUserContext } from '../context/user.svelte.js';
11
10
  import type { Route } from '../types.js';
12
11
  import { onMount, type Snippet } from 'svelte';
13
12
  import { setAPIProxyContext } from '../context/api-proxy.svelte.js';
13
+ import { setTitleContext } from '../context/title.js';
14
14
 
15
15
  type Props = {
16
16
  routes: Record<string, Route[]>;
@@ -28,7 +28,7 @@
28
28
  setConfigContext(config);
29
29
  // svelte-ignore state_referenced_locally
30
30
  setUserContext(user);
31
- createContext('title', '[untitled]');
31
+ setTitleContext('[untitled]');
32
32
  setAPIProxyContext();
33
33
 
34
34
  const locale = $derived(setLocaleContext(initialeLocale));
@@ -13,7 +13,7 @@
13
13
  <style>
14
14
  .rz-fields-preview__trigger {
15
15
  display: block;
16
- background-color: var(--rz-group-preview-bg);
16
+ background-color: light-dark(hsl(var(--rz-gray-16)), hsl(var(--rz-gray-3)));
17
17
  width: 100%;
18
18
  text-align: left;
19
19
  }
@@ -1,11 +1,11 @@
1
1
  export declare const Field: {
2
2
  Label: import("svelte").Component<{
3
- config?: import("../../../fields/types").SimplerField<import("../../../types").FormField>;
3
+ config?: import("../../../types").SimplerField<import("../../../types").FormField>;
4
4
  children?: import("svelte").Snippet;
5
5
  for?: string;
6
6
  }, {}, "">;
7
7
  Hint: import("svelte").Component<{
8
- config: import("../../../fields/types").SimplerField<import("../../../types").FormField>;
8
+ config: import("../../../types").SimplerField<import("../../../types").FormField>;
9
9
  }, {}, "">;
10
10
  LabelFor: import("svelte").Component<{
11
11
  config: import("../../../types").FormField;
@@ -9,9 +9,10 @@
9
9
  class?: string;
10
10
  };
11
11
  const { url, class: className, mimeType }: Props = $props();
12
+
12
13
  const mimeTypeResolved = $derived.by(() => {
13
14
  if (mimeType) return mimeType;
14
- if (url) return getMimeTypeFromExtension(url) || '';
15
+ if (url) return getMimeTypeFromExtension(url.split('.').pop() || '') || '';
15
16
  return '';
16
17
  });
17
18
  </script>
@@ -35,7 +36,7 @@
35
36
 
36
37
  <style>
37
38
  :root {
38
- --rz-upload-preview-cell-bg: light-dark(hsl(var(--rz-gray-11)), hsl(var(--rz-gray-0)));
39
+ --rz-upload-preview-cell-bg: light-dark(hsl(var(--rz-gray-15)), hsl(var(--rz-gray-0)));
39
40
  --rz-upload-preview-cell-fit: contain;
40
41
  }
41
42
 
@@ -26,6 +26,7 @@
26
26
  const deleteFile = () => {
27
27
  preview = null;
28
28
  file = null;
29
+ form.setValue('url', null);
29
30
  form.setValue('file', null);
30
31
  form.setValue('filename', null);
31
32
  form.setValue('mimeType', null);
@@ -62,12 +63,6 @@
62
63
  form.setValue('file', file);
63
64
  }
64
65
  });
65
-
66
- $effect(() => {
67
- if (preview && form.values._thumbnail !== preview) {
68
- form.setValue(`_thumbnail`, preview);
69
- }
70
- });
71
66
  </script>
72
67
 
73
68
  <div class="rz-doc-upload-header">
@@ -77,7 +72,7 @@
77
72
  {#if form.values.mimeType.includes('image')}
78
73
  <div class="rz-doc-upload-header__prewiew-grid">
79
74
  {#key form.values.title}
80
- <img src={form.values.url || form.values._thumbnail} alt="preview" />
75
+ <img src={form.values.url || form.values._thumbnail || preview} alt="preview" />
81
76
  {/key}
82
77
  </div>
83
78
  {:else}
@@ -5,8 +5,8 @@
5
5
  import { Toaster } from '../../ui/sonner';
6
6
  import { setAPIProxyContext } from '../../../context/api-proxy.svelte.js';
7
7
  import { setConfigContext } from '../../../context/config.svelte.js';
8
- import createContext from '../../../context/createContext.svelte.js';
9
8
  import { setLocaleContext } from '../../../context/locale.svelte.js';
9
+ import { setTitleContext } from '../../../context/title';
10
10
  import { setUserContext } from '../../../context/user.svelte.js';
11
11
  import { toKebabCase } from '../../../../util/string';
12
12
  import ScrollArea from '../../ui/scroll-area/scroll-area.svelte';
@@ -42,7 +42,7 @@
42
42
  setUserContext(user);
43
43
  // svelte-ignore state_referenced_locally
44
44
  setLocaleContext(initialLocale);
45
- createContext('title', '[untitled]');
45
+ setTitleContext('[untitled]');
46
46
  </script>
47
47
 
48
48
  <Toaster />
@@ -1,18 +1,18 @@
1
1
  <script lang="ts">
2
2
  import { page } from '$app/state';
3
3
  import { getConfigContext } from '../../../context/config.svelte.js';
4
+ import { getTitleContext } from '../../../context/title';
4
5
  import type { Route } from '../../../../types';
5
- import { getContext } from 'svelte';
6
6
  import Button from '../button/button.svelte';
7
7
 
8
8
  const config = getConfigContext();
9
- const titleContext = getContext<{ value: string }>('title');
9
+ const titleContext = getTitleContext();
10
10
 
11
11
  const aria = $derived<Route[]>(page.data.aria);
12
12
  </script>
13
13
 
14
14
  <div class="rz-aria">
15
- {#each aria as route}
15
+ {#each aria as route, index (index)}
16
16
  {@const Icon = config.raw.icons[route.icon]}
17
17
  {#if route.url}
18
18
  <Button variant="text" icon={Icon} href={route.url}>
@@ -25,8 +25,13 @@
25
25
  {/each}
26
26
  </div>
27
27
 
28
- <style>
29
- .rz-aria {
28
+ <style>/**************************************/
29
+
30
+ /* Font */
31
+
32
+ /**************************************/
33
+
34
+ .rz-aria {
30
35
  display: flex;
31
36
  align-items: center;
32
37
  gap: var(--rz-size-3);
@@ -40,7 +45,8 @@
40
45
  opacity: 1;
41
46
  }
42
47
  }
43
- .rz-aria__last {
48
+
49
+ .rz-aria__last {
44
50
  overflow: hidden;
45
51
  display: -webkit-box;
46
52
  -webkit-box-orient: vertical;
@@ -34,8 +34,8 @@
34
34
  <style>
35
35
  /** */
36
36
  :root {
37
- --rz-card-hover-bg: light-dark(hsl(var(--rz-gray-14)), hsl(var(--rz-gray-4)));
38
- --rz-card-bg: light-dark(hsl(var(--rz-gray-16)), hsl(var(--rz-gray-3)));
37
+ --rz-card-hover-bg: light-dark(hsl(var(--rz-gray-18)), hsl(var(--rz-gray-4)));
38
+ --rz-card-bg: light-dark(hsl(var(--rz-gray-17)), hsl(var(--rz-gray-3)));
39
39
  }
40
40
 
41
41
  .rz-document-card {
@@ -13,16 +13,18 @@
13
13
  open = $bindable(false),
14
14
  ref = $bindable(null),
15
15
  value = $bindable(''),
16
+ preventScroll = true,
16
17
  children,
17
18
  ...restProps
18
19
  }: WithoutChildrenOrChild<DialogPrimitive.RootProps> &
19
20
  WithoutChildrenOrChild<CommandPrimitive.RootProps> & {
20
21
  children: Snippet;
22
+ preventScroll?: boolean;
21
23
  } = $props();
22
24
  </script>
23
25
 
24
26
  <Dialog.Root bind:open {...restProps}>
25
- <Dialog.Content class="rz-command-dialog-content">
27
+ <Dialog.Content class="rz-command-dialog-content" {preventScroll}>
26
28
  <Command
27
29
  class="rz-command-dialog-content__command"
28
30
  {...restProps}
@@ -3,6 +3,7 @@ import type { Snippet } from 'svelte';
3
3
  import './command-dialog.css';
4
4
  type $$ComponentProps = WithoutChildrenOrChild<DialogPrimitive.RootProps> & WithoutChildrenOrChild<CommandPrimitive.RootProps> & {
5
5
  children: Snippet;
6
+ preventScroll?: boolean;
6
7
  };
7
8
  declare const CommandDialog: import("svelte").Component<$$ComponentProps, {}, "value" | "ref" | "open">;
8
9
  type CommandDialog = ReturnType<typeof CommandDialog>;
@@ -1,11 +1,13 @@
1
1
  export declare function setAPIProxyContext(key?: symbol): {
2
2
  getRessource: <T>(url: string, options?: GetResourcesOptions<T>) => Resource<T>;
3
3
  invalidateAll: () => void;
4
+ invalidate: (pattern: string | RegExp) => void;
4
5
  readonly urls: string[];
5
6
  };
6
7
  export declare function getAPIProxyContext(key?: symbol): {
7
8
  getRessource: <T>(url: string, options?: GetResourcesOptions<T>) => Resource<T>;
8
9
  invalidateAll: () => void;
10
+ invalidate: (pattern: string | RegExp) => void;
9
11
  readonly urls: string[];
10
12
  };
11
13
  export declare const API_PROXY: {
@@ -63,11 +63,19 @@ function createAPIProxy() {
63
63
  resource.refresh();
64
64
  });
65
65
  }
66
+ function invalidate(pattern) {
67
+ resources.forEach((resource, url) => {
68
+ if (typeof pattern === 'string' ? url.includes(pattern) : pattern.test(url)) {
69
+ resource.refresh();
70
+ }
71
+ });
72
+ }
66
73
  return {
67
74
  getRessource,
68
75
  invalidateAll,
76
+ invalidate,
69
77
  get urls() {
70
- return Array.from(resources.entries()).map(([url, resource]) => url);
78
+ return Array.from(resources.entries()).map(([url]) => url);
71
79
  }
72
80
  };
73
81
  }
@@ -22,6 +22,7 @@ import { getAPIProxyContext } from './api-proxy.svelte.js';
22
22
  import { getCollectionContext } from './collection.svelte.js';
23
23
  import { setErrorsContext } from './errors.svelte.js';
24
24
  import { getLocaleContext } from './locale.svelte.js';
25
+ import { getTitleContext } from './title.js';
25
26
  import { getUserContext } from './user.svelte.js';
26
27
  function createDocumentFormState({ initial, config, readOnly, key, onNestedDocumentCreated, afterSuccess, onDataChange, beforeSubmit, beforeRedirect, onFieldFocus }) {
27
28
  //
@@ -42,7 +43,7 @@ function createDocumentFormState({ initial, config, readOnly, key, onNestedDocum
42
43
  const nestedLevel = initLevel();
43
44
  const isLiveEdit = !!onDataChange;
44
45
  const locale = getLocaleContext();
45
- const titleContext = getContext('title');
46
+ const titleContext = getTitleContext();
46
47
  const initialTitle = initTitle();
47
48
  let title = $state(initialTitle);
48
49
  const apiProxy = getAPIProxyContext();
@@ -497,6 +498,8 @@ function createDocumentFormState({ initial, config, readOnly, key, onNestedDocum
497
498
  async function handleSuccess(data) {
498
499
  const redirect = data?.redirectUrl || false;
499
500
  const message = data?.message || t__('common.generic_success');
501
+ // Invalidate all queries to ensure data consistency across the app
502
+ apiProxy.invalidate(documentConfig.slug);
500
503
  if (redirect) {
501
504
  if (beforeRedirect) {
502
505
  const shouldRedirect = await beforeRedirect(data);
@@ -509,8 +512,6 @@ function createDocumentFormState({ initial, config, readOnly, key, onNestedDocum
509
512
  // Assign documents returned from the server to the form state
510
513
  doc = (data?.document || doc);
511
514
  initialDoc = doc;
512
- // Invalidate all queries to ensure data consistency across the app
513
- apiProxy.invalidateAll();
514
515
  toast.success(message);
515
516
  // Callbacks
516
517
  if (nestedLevel !== 0) {
@@ -594,7 +595,7 @@ function createDocumentFormState({ initial, config, readOnly, key, onNestedDocum
594
595
  actionSuffix = '?/update';
595
596
  }
596
597
  // Add a redirect parameter if we're in a nested form ex: relation creation
597
- // to prevent reidrect after creation
598
+ // to prevent redirect after creation
598
599
  const redirectParam = nestedLevel > 0 ? `&${PARAMS.REDIRECT}=false` : '';
599
600
  // Combine all parts to form the final action URL
600
601
  return `${panelUri}${actionSuffix}${redirectParam}`;
@@ -0,0 +1,7 @@
1
+ export declare const TITLE_CTX = "rime.document.title";
2
+ export declare function getTitleContext(): {
3
+ value: string;
4
+ };
5
+ export declare function setTitleContext(title: string): {
6
+ value: string;
7
+ };
@@ -0,0 +1,9 @@
1
+ import { getContext } from 'svelte';
2
+ import createContext from './createContext.svelte';
3
+ export const TITLE_CTX = 'rime.document.title';
4
+ export function getTitleContext() {
5
+ return getContext(TITLE_CTX);
6
+ }
7
+ export function setTitleContext(title) {
8
+ return createContext(TITLE_CTX, title);
9
+ }
@@ -17,8 +17,8 @@
17
17
  import PageHeader from '../../components/ui/page-header/PageHeader.svelte';
18
18
  import { setCollectionContext } from '../../context/collection.svelte.js';
19
19
  import { getConfigContext } from '../../context/config.svelte.js';
20
+ import { getTitleContext } from '../../context/title';
20
21
  import { CopyPlus } from '@lucide/svelte';
21
- import { getContext } from 'svelte';
22
22
 
23
23
  type Props = {
24
24
  slug: string;
@@ -60,7 +60,7 @@
60
60
  collection.docs = data.docs;
61
61
  });
62
62
 
63
- const titleContext = getContext<{ value: string }>('title');
63
+ const titleContext = getTitleContext();
64
64
 
65
65
  $effect(() => {
66
66
  titleContext.value = collection.config.label.plural;
package/dist/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export type { User } from './core/collections/auth/types.js';
2
2
  export type { AdditionalStaffConfig, Area, AreaHooks, BuiltArea, BuiltCollection, BuiltConfig, BuiltConfigClient, Collection, CollectionHooks, Config, CustomPanelRoute, ImageSizesConfig, LocaleConfig, LocalizationConfig, RouteConfig } from './core/config/types.js';
3
3
  export type { AreaSlug, BaseDoc, CollectionSlug, GenericBlock, GenericDoc, Prototype, PrototypeSlug, UploadDoc } from './core/types/doc.js';
4
- export type { Field, FormField, Option, RelationValue } from './fields/types.js';
4
+ export type * from './fields/types.js';
5
5
  export type { CollectionProps, FieldPanelTableConfig, FormErrors, Navigation, Route } from './panel/types.js';
6
6
  export type { DocumentFormContext } from './panel/context/documentForm.svelte.js';
7
7
  export type { JsonFile } from './core/collections/upload/upload.js';