rimecms 0.27.0 → 0.27.1
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.
- package/README.md +26 -24
- package/dist/core/collections/auth/fields.d.ts +40 -40
- package/dist/core/fields/builders/select.js +1 -0
- package/dist/core/i18n/en/fields.d.ts +1 -0
- package/dist/core/i18n/en/fields.js +2 -1
- package/dist/core/i18n/fr/fields.d.ts +1 -0
- package/dist/core/i18n/fr/fields.js +2 -1
- package/dist/core/operations/hooks/before-read/set-document-thumbnail.server.js +2 -2
- package/dist/core/operations/hooks/before-read/sort-document-props.server.js +3 -1
- package/dist/fields/relation/component/Relation.svelte +16 -9
- package/dist/fields/relation/component/Relation.svelte.d.ts +2 -1
- package/dist/fields/relation/index.d.ts +1 -1
- package/dist/fields/rich-text/client.d.ts +9 -16
- package/dist/fields/rich-text/client.js +1 -1
- package/dist/fields/rich-text/component/RichText.svelte +1 -1
- package/dist/fields/rich-text/core/{config-builders.js → build-editor-config.js} +4 -0
- package/dist/fields/rich-text/core/features/fields/extension.js +2 -2
- package/dist/fields/rich-text/core/features/fields/index.d.ts +1 -1
- package/dist/fields/rich-text/core/features/fields/index.js +4 -2
- package/dist/fields/rich-text/core/features/link/component/link-selector.svelte +7 -7
- package/dist/fields/rich-text/core/features/link/index.d.ts +1 -4
- package/dist/fields/rich-text/core/features/resource/index.js +17 -11
- package/dist/fields/rich-text/core/features/resource/resource-extension.d.ts +2 -2
- package/dist/fields/rich-text/core/features/resource/resource-extension.js +5 -3
- package/dist/fields/rich-text/core/features/resource/resource.svelte +17 -19
- package/dist/fields/rich-text/core/features/upload/index.d.ts +2 -5
- package/dist/fields/rich-text/core/features/upload/index.js +15 -11
- package/dist/fields/rich-text/core/features/upload/upload-extension.d.ts +1 -2
- package/dist/fields/rich-text/core/features/upload/upload-extension.js +4 -1
- package/dist/fields/rich-text/core/features/upload/upload.svelte +13 -8
- package/dist/fields/types.d.ts +3 -0
- package/dist/panel/components/Root.svelte +2 -2
- package/dist/panel/components/fields/FieldsPreviewTrigger.svelte +1 -1
- package/dist/panel/components/fields/index.d.ts +2 -2
- package/dist/panel/components/sections/collection/upload-thumb-cell/UploadThumbCell.svelte +3 -2
- package/dist/panel/components/sections/document/upload-header/UploadHeader.svelte +2 -7
- package/dist/panel/components/sections/live/SidePanel.svelte +2 -2
- package/dist/panel/components/ui/breadcrumb/BreadCrumb.svelte +12 -6
- package/dist/panel/components/ui/card-document/card-document.svelte +2 -2
- package/dist/panel/components/ui/command/command-dialog.svelte +3 -1
- package/dist/panel/components/ui/command/command-dialog.svelte.d.ts +1 -0
- package/dist/panel/context/api-proxy.svelte.d.ts +2 -0
- package/dist/panel/context/api-proxy.svelte.js +9 -1
- package/dist/panel/context/documentForm.svelte.js +5 -4
- package/dist/panel/context/title.d.ts +7 -0
- package/dist/panel/context/title.js +9 -0
- package/dist/panel/pages/collection/Collection.svelte +2 -2
- package/dist/types.d.ts +1 -1
- package/package.json +3 -3
- /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
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
13
|
-
|
|
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:
|
|
29
|
+
return [{ tag: this.name }];
|
|
28
30
|
},
|
|
29
31
|
renderHTML({ HTMLAttributes }) {
|
|
30
|
-
return [
|
|
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.
|
|
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
|
|
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:
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
4
|
-
|
|
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
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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.
|
|
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>
|
package/dist/fields/types.d.ts
CHANGED
|
@@ -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
|
-
|
|
31
|
+
setTitleContext('[untitled]');
|
|
32
32
|
setAPIProxyContext();
|
|
33
33
|
|
|
34
34
|
const locale = $derived(setLocaleContext(initialeLocale));
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export declare const Field: {
|
|
2
2
|
Label: import("svelte").Component<{
|
|
3
|
-
config?: import("../../../
|
|
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("../../../
|
|
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-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
38
|
-
--rz-card-bg: light-dark(hsl(var(--rz-gray-
|
|
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
|
|
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 =
|
|
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
|
|
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,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 =
|
|
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
|
|
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';
|