rimecms 0.26.9 → 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.
Files changed (81) 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/group/component/Group.svelte +1 -0
  11. package/dist/fields/group/index.d.ts +12 -8
  12. package/dist/fields/group/index.js +4 -0
  13. package/dist/fields/link/component/RessourceInput.svelte +2 -2
  14. package/dist/fields/relation/component/Cell.svelte +2 -2
  15. package/dist/fields/relation/component/Relation.svelte +18 -11
  16. package/dist/fields/relation/component/Relation.svelte.d.ts +2 -1
  17. package/dist/fields/relation/component/upload/Browse.svelte +2 -2
  18. package/dist/fields/relation/index.d.ts +1 -1
  19. package/dist/fields/rich-text/client.d.ts +9 -9
  20. package/dist/fields/rich-text/client.js +1 -1
  21. package/dist/fields/rich-text/component/RichText.svelte +1 -1
  22. package/dist/fields/rich-text/core/{config-builders.js → build-editor-config.js} +9 -2
  23. package/dist/fields/rich-text/core/features/fields/extension.d.ts +10 -3
  24. package/dist/fields/rich-text/core/features/fields/extension.js +3 -8
  25. package/dist/fields/rich-text/core/features/fields/fields.css +4 -0
  26. package/dist/fields/rich-text/core/features/fields/fields.svelte +13 -6
  27. package/dist/fields/rich-text/core/features/fields/fields.svelte.d.ts +4 -1
  28. package/dist/fields/rich-text/core/features/fields/index.d.ts +7 -5
  29. package/dist/fields/rich-text/core/features/fields/index.js +4 -3
  30. package/dist/fields/rich-text/core/features/link/component/link-selector.svelte +9 -13
  31. package/dist/fields/rich-text/core/features/link/index.d.ts +1 -4
  32. package/dist/fields/rich-text/core/features/resource/index.d.ts +2 -5
  33. package/dist/fields/rich-text/core/features/resource/index.js +17 -12
  34. package/dist/fields/rich-text/core/features/resource/resource-extension.d.ts +7 -2
  35. package/dist/fields/rich-text/core/features/resource/resource-extension.js +6 -10
  36. package/dist/fields/rich-text/core/features/resource/resource.svelte +19 -21
  37. package/dist/fields/rich-text/core/features/upload/index.d.ts +2 -5
  38. package/dist/fields/rich-text/core/features/upload/index.js +15 -12
  39. package/dist/fields/rich-text/core/features/upload/upload-extension.d.ts +13 -1
  40. package/dist/fields/rich-text/core/features/upload/upload-extension.js +6 -11
  41. package/dist/fields/rich-text/core/features/upload/upload.svelte +15 -10
  42. package/dist/fields/rich-text/core/svelte/node-view-renderer.svelte.js +10 -5
  43. package/dist/fields/rich-text/core/svelte/node-view-wrapper.svelte +1 -0
  44. package/dist/fields/rich-text/core/svelte/renderer.d.ts +1 -1
  45. package/dist/fields/types.d.ts +13 -0
  46. package/dist/panel/components/Root.svelte +4 -4
  47. package/dist/panel/components/fields/FieldsPreview.svelte +34 -27
  48. package/dist/panel/components/fields/FieldsPreview.svelte.d.ts +5 -6
  49. package/dist/panel/components/fields/FieldsPreviewTrigger.svelte +1 -1
  50. package/dist/panel/components/fields/index.d.ts +2 -2
  51. package/dist/panel/components/sections/collection/folder/FolderEdit.svelte +0 -2
  52. package/dist/panel/components/sections/collection/folder/FolderWithActions.svelte +2 -2
  53. package/dist/panel/components/sections/collection/grid/create-directory-dialog/CreateDirectoryDialog.svelte +0 -2
  54. package/dist/panel/components/sections/collection/upload-thumb-cell/UploadThumbCell.svelte +3 -2
  55. package/dist/panel/components/sections/document/Header.svelte +1 -0
  56. package/dist/panel/components/sections/document/Settings.svelte +3 -2
  57. package/dist/panel/components/sections/document/Settings.svelte.d.ts +2 -1
  58. package/dist/panel/components/sections/document/upload-header/UploadHeader.svelte +2 -7
  59. package/dist/panel/components/sections/live/SidePanel.svelte +7 -9
  60. package/dist/panel/components/ui/breadcrumb/BreadCrumb.svelte +12 -6
  61. package/dist/panel/components/ui/card-document/card-document.svelte +2 -2
  62. package/dist/panel/components/ui/command/command-dialog.svelte +3 -1
  63. package/dist/panel/components/ui/command/command-dialog.svelte.d.ts +1 -0
  64. package/dist/panel/context/api-proxy.svelte.d.ts +2 -1
  65. package/dist/panel/context/api-proxy.svelte.js +9 -3
  66. package/dist/panel/context/documentForm.svelte.d.ts +2 -2
  67. package/dist/panel/context/documentForm.svelte.js +7 -6
  68. package/dist/panel/context/form.svelte.js +0 -1
  69. package/dist/panel/context/locale.svelte.d.ts +1 -0
  70. package/dist/panel/context/locale.svelte.js +3 -3
  71. package/dist/panel/context/title.d.ts +7 -0
  72. package/dist/panel/context/title.js +9 -0
  73. package/dist/panel/pages/area/Area.svelte +1 -4
  74. package/dist/panel/pages/area/AreaVersionsDoc.svelte +0 -3
  75. package/dist/panel/pages/collection/Collection.svelte +2 -2
  76. package/dist/panel/pages/collection-document/CollectionDocVersions.svelte +0 -5
  77. package/dist/panel/pages/collection-document/CollectionDocument.svelte +0 -3
  78. package/dist/panel/pages/live/Live.svelte +3 -8
  79. package/dist/types.d.ts +1 -1
  80. package/package.json +4 -4
  81. /package/dist/fields/rich-text/core/{config-builders.d.ts → build-editor-config.d.ts} +0 -0
@@ -1,25 +1,25 @@
1
1
  import type { Level } from '@tiptap/extension-heading';
2
- import { FieldsFeature } from './core/features/fields/index.js';
3
- import { LinkFeature } from './core/features/link/index.js';
4
- import { ResourceFeature } from './core/features/resource/index.js';
2
+ import { type FieldsFeatureOptions } from './core/features/fields/index.js';
3
+ import { type LinkFeatureOptions } from './core/features/link/index.js';
4
+ import type { ResourceFeatureExtensionOptions } from './core/features/resource/resource-extension.js';
5
5
  import type { RichTextResource } from './core/features/resource/types.js';
6
- import { UploadFeature } from './core/features/upload/index.js';
6
+ import type { UploadFeatureExtensionOptions } from './core/features/upload/upload-extension.js';
7
7
  import RenderRichText from './core/render-rich-text.svelte';
8
8
  import SvelteNodeViewRenderer from './core/svelte/node-view-renderer.svelte';
9
9
  import NodeViewWrapper from './core/svelte/node-view-wrapper.svelte';
10
10
  import type { RichTextNodeRenderer, RichTextNodeRendererProps } from './core/types.js';
11
11
  import { richTextJSONToText } from './index.js';
12
- export declare const fields: (args: Parameters<typeof FieldsFeature>[0]) => import("./core/types.js").RichTextFeature;
12
+ export declare const fields: (args: FieldsFeatureOptions) => import("./core/types.js").RichTextFeature;
13
13
  export declare const bold: () => import("./core/types.js").RichTextFeature;
14
14
  export declare const bulletList: () => import("./core/types.js").RichTextFeature;
15
15
  export declare const heading: (...levels: Level[]) => import("./core/types.js").RichTextFeature;
16
16
  export declare const hr: () => import("./core/types.js").RichTextFeature;
17
- export declare const link: (options?: Parameters<typeof LinkFeature>[0]) => import("./core/types.js").RichTextFeature;
17
+ export declare const link: (options?: Partial<LinkFeatureOptions>) => import("./core/types.js").RichTextFeature;
18
18
  export declare const paragraph: () => import("./core/types.js").RichTextFeature;
19
- export declare const orederedList: () => import("./core/types.js").RichTextFeature;
19
+ export declare const orderedList: () => import("./core/types.js").RichTextFeature;
20
20
  export declare const blockquote: () => import("./core/types.js").RichTextFeature;
21
21
  export declare const italic: () => import("./core/types.js").RichTextFeature;
22
- export declare const upload: (args: Parameters<typeof UploadFeature>[0]) => import("./core/types.js").RichTextFeature;
23
- export declare const resource: (args: Parameters<typeof ResourceFeature>[0]) => import("./core/types.js").RichTextFeature;
22
+ export declare const upload: (args: UploadFeatureExtensionOptions) => import("./core/types.js").RichTextFeature;
23
+ export declare const resource: (args: ResourceFeatureExtensionOptions) => import("./core/types.js").RichTextFeature;
24
24
  export { NodeViewWrapper, RenderRichText, richTextJSONToText, SvelteNodeViewRenderer };
25
25
  export type { RichTextNodeRenderer, RichTextNodeRendererProps, RichTextResource };
@@ -21,7 +21,7 @@ export const heading = (...levels) => HeadingFeature(...levels);
21
21
  export const hr = () => HorizontalRuleFeature;
22
22
  export const link = (options) => LinkFeature(options);
23
23
  export const paragraph = () => ParagraphFeature;
24
- export const orederedList = () => OrderedListFeature;
24
+ export const orderedList = () => OrderedListFeature;
25
25
  export const blockquote = () => BlockquoteFeature;
26
26
  export const italic = () => ItalicFeature;
27
27
  export const upload = (args) => UploadFeature(args);
@@ -3,7 +3,7 @@
3
3
  import { root } from '../../../panel/components/fields/root.svelte.js';
4
4
  import { Editor, type JSONContent } from '@tiptap/core';
5
5
  import { onMount } from 'svelte';
6
- import { buildEditorConfig } from '../core/config-builders.js';
6
+ import { buildEditorConfig } from '../core/build-editor-config.js';
7
7
  import type { RichTextFeature } from '../core/types';
8
8
  import { hasSuggestion } from '../util.js';
9
9
  import EditorBubbleMenu from './bubble-menu/bubble-menu.svelte';
@@ -1,4 +1,5 @@
1
1
  import { t__ } from '../../../core/i18n/index.js';
2
+ import { getTitleContext, TITLE_CTX } from '../../../panel/context/title.js';
2
3
  import Document from '@tiptap/extension-document';
3
4
  import HardBreak from '@tiptap/extension-hard-break';
4
5
  import { ListItem } from '@tiptap/extension-list';
@@ -7,6 +8,7 @@ import Typography from '@tiptap/extension-typography';
7
8
  import { Dropcursor, Gapcursor, Placeholder, UndoRedo } from '@tiptap/extensions';
8
9
  import { API_PROXY, getAPIProxyContext } from '../../../panel/context/api-proxy.svelte.js';
9
10
  import { CONFIG_CTX, getConfigContext } from '../../../panel/context/config.svelte.js';
11
+ import { getLocaleContext, LOCALE_CTX } from '../../../panel/context/locale.svelte.js';
10
12
  import { getUserContext, USER_CTX } from '../../../panel/context/user.svelte.js';
11
13
  import { hasSuggestion } from '../util.js';
12
14
  import { CurrentNodeAttribute } from './extensions/current-node/current-node.js';
@@ -60,12 +62,17 @@ export function buildEditorConfig(args) {
60
62
  if ('addNodeView' in feature.extension.config) {
61
63
  const originalAddOption = feature.extension.config.addOptions || (() => ({}));
62
64
  const contexts = new Map();
65
+ const localeContext = getLocaleContext();
63
66
  const configContext = getConfigContext();
64
- const apiProxyContext = getAPIProxyContext(API_PROXY.DOCUMENT);
67
+ const apiProxyContext = getAPIProxyContext();
65
68
  const userContext = getUserContext();
69
+ const titleContext = getTitleContext();
70
+ contexts.set(TITLE_CTX, titleContext);
66
71
  contexts.set(CONFIG_CTX, configContext);
67
- contexts.set(API_PROXY.DOCUMENT, apiProxyContext);
72
+ contexts.set(CONFIG_CTX, configContext);
73
+ contexts.set(API_PROXY.ROOT, apiProxyContext);
68
74
  contexts.set(USER_CTX, userContext);
75
+ contexts.set(LOCALE_CTX, localeContext);
69
76
  feature.extension.config.addOptions = () => {
70
77
  // @ts-expect-error
71
78
  return { ...originalAddOption(), contexts };
@@ -1,10 +1,17 @@
1
+ import type { FieldBuilder } from '../../../../../core/fields/builders';
2
+ import type { FieldsPreviewProps } from '../../../../types';
1
3
  import type { Dic } from '../../../../../util/types.js';
2
4
  import { Node } from '@tiptap/core';
5
+ import type { Component } from 'svelte';
3
6
  declare module '@tiptap/core' {
4
7
  interface Commands<ReturnType> {
5
- sheet: {
6
- insertSheet: (attributes: Dic) => ReturnType;
8
+ 'richt-text-fields': {
9
+ insertSheet: (attributes?: Dic) => ReturnType;
7
10
  };
8
11
  }
9
12
  }
10
- export declare const FieldsExtension: Node<any, any>;
13
+ export interface FieldsFeatureExtensionOptions {
14
+ fields: FieldBuilder[];
15
+ preview?: Component<FieldsPreviewProps>;
16
+ }
17
+ export declare const FieldsExtension: Node<FieldsFeatureExtensionOptions, any>;
@@ -5,13 +5,8 @@ export const FieldsExtension = Node.create({
5
5
  name: 'richt-text-fields',
6
6
  group: 'block',
7
7
  atom: true,
8
- draggable: true, // Optional: to make the node draggable
8
+ draggable: true,
9
9
  inline: false,
10
- addOptions() {
11
- return {
12
- fields: null
13
- };
14
- },
15
10
  addAttributes() {
16
11
  return ['json'].reduce((acc, key) => {
17
12
  acc[key] = { default: null };
@@ -29,10 +24,10 @@ export const FieldsExtension = Node.create({
29
24
  };
30
25
  },
31
26
  parseHTML() {
32
- return [{ tag: 'richt-text-fields' }];
27
+ return [{ tag: this.name }];
33
28
  },
34
29
  renderHTML({ HTMLAttributes }) {
35
- return ['richt-text-fields', mergeAttributes(HTMLAttributes)];
30
+ return [this.name, mergeAttributes(HTMLAttributes)];
36
31
  },
37
32
  addNodeView() {
38
33
  return SvelteNodeViewRenderer(FieldsComponent);
@@ -25,4 +25,8 @@
25
25
  .rz-render-fields-preview__row:last-child .rz-render-fields-preview__name {
26
26
  border-bottom-left-radius: var(--rz-radius-lg);
27
27
  }
28
+
29
+ &.rz-rich-text-fields-preview--custom {
30
+ max-width: unset;
31
+ }
28
32
  }
@@ -1,13 +1,12 @@
1
1
  <script lang="ts">
2
- import { page } from '$app/state';
3
2
  import { FieldBuilder, FormFieldBuilder } from '../../../../../core/fields/builders';
3
+ import type { FieldsPreviewProps } from '../../../../types';
4
4
  import FieldsPreview from '../../../../../panel/components/fields/FieldsPreview.svelte';
5
5
  import FieldsPreviewTrigger from '../../../../../panel/components/fields/FieldsPreviewTrigger.svelte';
6
6
  import * as Sheet from '../../../../../panel/components/ui/sheet/index.js';
7
7
  import { setFormContext } from '../../../../../panel/context/form.svelte';
8
- import { setLocaleContext } from '../../../../../panel/context/locale.svelte';
9
8
  import type { NodeViewProps } from '@tiptap/core';
10
- import { onMount } from 'svelte';
9
+ import { onMount, type Component } from 'svelte';
11
10
  import NodeViewWrapper from '../../svelte/node-view-wrapper.svelte';
12
11
  import './fields.css';
13
12
 
@@ -15,6 +14,7 @@
15
14
  extension: {
16
15
  options: {
17
16
  fields: FieldBuilder[];
17
+ preview?: Component<FieldsPreviewProps>;
18
18
  };
19
19
  };
20
20
  };
@@ -22,7 +22,6 @@
22
22
  let { node, extension, updateAttributes }: Props = $props();
23
23
  let isSheetOpen = $state(false);
24
24
 
25
- setLocaleContext(page.data.locale || 'en');
26
25
  // svelte-ignore state_referenced_locally
27
26
  const form = setFormContext(node.attrs.json || {}, 'fields');
28
27
 
@@ -43,19 +42,27 @@
43
42
  const previewFields = $derived.by(() => {
44
43
  return extension.options.fields.filter((field) => field instanceof FormFieldBuilder);
45
44
  });
45
+
46
+ const classModifiers = $derived(
47
+ extension.options.preview ? 'rz-rich-text-fields-preview--custom' : ''
48
+ );
46
49
  </script>
47
50
 
48
51
  <NodeViewWrapper>
49
- <FieldsPreviewTrigger class="rz-rich-text-fields-preview" onclick={() => (isSheetOpen = true)}>
52
+ <FieldsPreviewTrigger
53
+ class="rz-rich-text-fields-preview {classModifiers}"
54
+ onclick={() => (isSheetOpen = true)}
55
+ >
50
56
  <FieldsPreview
51
57
  fields={previewFields}
52
58
  getField={(field) => form.useField(field.name, field.raw)}
59
+ preview={extension.options.preview}
53
60
  />
54
61
  </FieldsPreviewTrigger>
55
62
  </NodeViewWrapper>
56
63
 
57
64
  <Sheet.Root bind:open={isSheetOpen}>
58
- <Sheet.Content side="right" class="rz-rich-text-sheet">
65
+ <Sheet.Content preventScroll={false} side="right" class="rz-rich-text-sheet">
59
66
  {#each previewFields || [] as field, index (index)}
60
67
  {@const FieldComponent = field.component}
61
68
  <FieldComponent path={field.raw.name} config={field.raw} {form} />
@@ -1,13 +1,16 @@
1
1
  import { FieldBuilder } from '../../../../../core/fields/builders';
2
+ import type { FieldsPreviewProps } from '../../../../types';
2
3
  import type { NodeViewProps } from '@tiptap/core';
4
+ import { type Component } from 'svelte';
3
5
  import './fields.css';
4
6
  type Props = Omit<NodeViewProps, 'extension'> & {
5
7
  extension: {
6
8
  options: {
7
9
  fields: FieldBuilder[];
10
+ preview?: Component<FieldsPreviewProps>;
8
11
  };
9
12
  };
10
13
  };
11
- declare const Fields: import("svelte").Component<Props, {}, "">;
14
+ declare const Fields: Component<Props, {}, "">;
12
15
  type Fields = ReturnType<typeof Fields>;
13
16
  export default Fields;
@@ -1,9 +1,11 @@
1
1
  import type { FieldBuilder } from '../../../../../core/fields/builders/field-builder.js';
2
+ import type { FieldsPreviewProps } from '../../../../types.js';
3
+ import type { Component } from 'svelte';
2
4
  import type { RichTextFeature } from '../../../../../types.js';
3
- type Args = {
5
+ export interface FieldsFeatureOptions {
4
6
  name: string;
5
- label: string;
7
+ label?: string;
6
8
  fields: FieldBuilder[];
7
- };
8
- export declare const FieldsFeature: (args: Args) => RichTextFeature;
9
- export {};
9
+ preview?: Component<FieldsPreviewProps>;
10
+ }
11
+ export declare const FieldsFeature: (args: FieldsFeatureOptions) => RichTextFeature;
@@ -3,13 +3,14 @@ import { FieldsExtension } from './extension.js';
3
3
  const fieldsFeatureNode = (args) => ({
4
4
  label: args.label || args.name,
5
5
  icon: SheetIcon,
6
- isActive: ({ editor }) => editor.isActive('richt-text-fields'),
6
+ isActive: ({ editor }) => editor.isActive('richt-text-fields-' + args.name),
7
7
  suggestion: {
8
- //@ts-expect-error annoying
9
8
  command: ({ editor }) => editor.chain().focus().insertSheet().run()
10
9
  }
11
10
  });
12
11
  export const FieldsFeature = (args) => ({
13
- extension: FieldsExtension.configure(args),
12
+ extension: FieldsExtension.configure({ fields: args.fields, preview: args.preview }).extend({
13
+ name: 'richt-text-fields-' + args.name
14
+ }),
14
15
  nodes: [fieldsFeatureNode(args)]
15
16
  });
@@ -6,11 +6,7 @@
6
6
  import Button from '../../../../../../panel/components/ui/button/button.svelte';
7
7
  import * as Command from '../../../../../../panel/components/ui/command/index.js';
8
8
  import Input from '../../../../../../panel/components/ui/input/input.svelte';
9
- import {
10
- API_PROXY,
11
- getAPIProxyContext,
12
- type Resource
13
- } from '../../../../../../panel/context/api-proxy.svelte.js';
9
+ import { getAPIProxyContext, type Resource } from '../../../../../../panel/context/api-proxy.svelte.js';
14
10
  import { getLocaleContext } from '../../../../../../panel/context/locale.svelte';
15
11
  import validate from '../../../../../../util/validate.js';
16
12
  import { CornerDownLeft, ExternalLink, Link2, Newspaper, Trash } from '@lucide/svelte';
@@ -40,18 +36,18 @@
40
36
  /**
41
37
  * Logic to get internal resources e.g., collections, areas
42
38
  */
43
- const APIProxy = getAPIProxyContext(API_PROXY.DOCUMENT);
39
+ const APIProxy = getAPIProxyContext();
44
40
  const locale = getLocaleContext();
45
41
  let resources = $state<Resource<LinkResource[]>[]>([]);
46
42
  let resourcesFlatMap = $state<LinkResource[]>();
43
+
47
44
  $effect(() => {
48
- if (Array.isArray(options?.resources) && !resources.length) {
49
- resources = options.resources.map((resourceOption) => {
50
- let params = resourceOption.query
51
- ? `?${resourceOption.query}&select=title,url`
52
- : '?select=title,url';
45
+ if (Array.isArray(options?.sources) && !resources.length) {
46
+ resources = options.sources.map((source) => {
47
+ const [slug, query] = source.split('?');
48
+ let params = query || '' ? `?${query}&select=title,url` : '?select=title,url';
53
49
  params = locale.code ? `${params}&locale=${locale.code}` : params;
54
- const url = `${apiUrl(resourceOption.slug)}${params}`;
50
+ const url = `${apiUrl(slug)}${params}`;
55
51
  return APIProxy.getRessource<LinkResource[]>(url, {
56
52
  transformData: (data) => {
57
53
  return 'docs' in data ? data.docs : 'doc' in data ? [data.doc] : data;
@@ -212,7 +208,7 @@
212
208
  />
213
209
  <Button size="icon-sm" variant="ghost" icon={Trash} onclick={onDelete} />
214
210
  {:else}
215
- {#if options?.resources?.length}
211
+ {#if options?.sources?.length}
216
212
  <Button
217
213
  onclick={() => (resourceDialogOpen = true)}
218
214
  size="icon-sm"
@@ -2,9 +2,6 @@ import type { PrototypeSlug } from '../../../../../types.js';
2
2
  import { type LinkOptions } from '@tiptap/extension-link';
3
3
  import type { RichTextFeature } from '../../types.js';
4
4
  export type LinkFeatureOptions = LinkOptions & {
5
- resources?: Array<{
6
- slug: PrototypeSlug;
7
- query?: string;
8
- }>;
5
+ sources?: Array<`${PrototypeSlug}${string}`>;
9
6
  };
10
7
  export declare const LinkFeature: (options?: Partial<LinkFeatureOptions>) => RichTextFeature;
@@ -1,6 +1,3 @@
1
- import type { PrototypeSlug } from '../../../../../types.js';
2
1
  import type { RichTextFeature } from '../../types.js';
3
- export declare const ResourceFeature: (args: {
4
- query?: string;
5
- slug: PrototypeSlug;
6
- }) => RichTextFeature;
2
+ import { type ResourceFeatureExtensionOptions } from './resource-extension.js';
3
+ export declare const ResourceFeature: (args: ResourceFeatureExtensionOptions) => RichTextFeature;
@@ -1,15 +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
- //@ts-expect-error annoying
9
- command: ({ editor }) => editor.chain().focus().insertResource().run()
10
- }
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
+ };
11
20
  };
12
- export const ResourceFeature = (args) => ({
13
- extension: Resource.configure(args),
14
- nodes: [resourceFeatureNode]
15
- });
@@ -1,10 +1,15 @@
1
+ import type { PrototypeSlug } from '../../../../../core/types/doc';
1
2
  import type { Dic } from '../../../../../util/types.js';
2
3
  import { Node } from '@tiptap/core';
3
4
  declare module '@tiptap/core' {
4
5
  interface Commands<ReturnType> {
5
6
  resource: {
6
- insertResource: (attributes: Dic) => ReturnType;
7
+ insertResource: (attributes?: Dic) => ReturnType;
7
8
  };
8
9
  }
9
10
  }
10
- export declare const Resource: Node<any, any>;
11
+ export interface ResourceFeatureExtensionOptions {
12
+ label?: string;
13
+ source: `${PrototypeSlug}${string}`;
14
+ }
15
+ export declare const Resource: Node<ResourceFeatureExtensionOptions, any>;
@@ -5,19 +5,15 @@ export const Resource = Node.create({
5
5
  name: 'resource',
6
6
  group: 'block',
7
7
  atom: true,
8
- draggable: true, // Optional: to make the node draggable
8
+ draggable: true,
9
9
  inline: false,
10
- addOptions() {
11
- return {
12
- query: null,
13
- _type: null
14
- };
15
- },
16
10
  addAttributes() {
17
11
  return ['id', 'title', '_type', '_thumbnail'].reduce((acc, key) => {
18
12
  acc[key] = { default: null };
19
13
  return acc;
20
- }, {});
14
+ },
15
+ // Fresh attributes allowing to show the dialog immediately after insertion
16
+ { _fresh: { default: true } });
21
17
  },
22
18
  addCommands() {
23
19
  return {
@@ -30,10 +26,10 @@ export const Resource = Node.create({
30
26
  };
31
27
  },
32
28
  parseHTML() {
33
- return [{ tag: 'richt-text-resource' }];
29
+ return [{ tag: this.name }];
34
30
  },
35
31
  renderHTML({ HTMLAttributes }) {
36
- return ['richt-text-resource', mergeAttributes(HTMLAttributes)];
32
+ return [this.name, mergeAttributes(HTMLAttributes)];
37
33
  },
38
34
  addNodeView() {
39
35
  return SvelteNodeViewRenderer(ResourceComponent);
@@ -4,7 +4,7 @@
4
4
  import Button from '../../../../../panel/components/ui/button/button.svelte';
5
5
  import CardResource from '../../../../../panel/components/ui/card-resource/card-resource.svelte';
6
6
  import * as Command from '../../../../../panel/components/ui/command/index.js';
7
- import { API_PROXY, getAPIProxyContext } from '../../../../../panel/context/api-proxy.svelte.js';
7
+ import { getAPIProxyContext } from '../../../../../panel/context/api-proxy.svelte.js';
8
8
  import type { NodeViewProps } from '@tiptap/core';
9
9
  import { onMount } from 'svelte';
10
10
  import NodeViewWrapper from '../../svelte/node-view-wrapper.svelte';
@@ -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
- const APIProxy = getAPIProxyContext(API_PROXY.DOCUMENT);
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,15 +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
- //@ts-expect-error insertMedia is defined by the extension
9
- command: ({ editor }) => editor.chain().focus().insertMedia().run()
10
- }
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
+ };
11
18
  };
12
- export const UploadFeature = (args) => ({
13
- extension: Upload.configure(args),
14
- nodes: [uploadFeatureNode]
15
- });
@@ -1,2 +1,14 @@
1
+ import type { CollectionSlug } from '../../../../../types';
2
+ import type { Dic } from '../../../../../util/types.js';
1
3
  import { Node } from '@tiptap/core';
2
- export declare const Upload: Node<any, any>;
4
+ export interface UploadFeatureExtensionOptions {
5
+ source: `${CollectionSlug}${string}`;
6
+ }
7
+ declare module '@tiptap/core' {
8
+ interface Commands<ReturnType> {
9
+ upload: {
10
+ insertUpload: (attributes?: Dic) => ReturnType;
11
+ };
12
+ }
13
+ }
14
+ export declare const Upload: Node<UploadFeatureExtensionOptions, any>;
@@ -5,25 +5,20 @@ export const Upload = Node.create({
5
5
  name: 'upload',
6
6
  group: 'block',
7
7
  atom: true,
8
- draggable: true, // Optional: to make the node draggable
8
+ draggable: true,
9
9
  inline: false,
10
- addOptions() {
11
- return {
12
- query: null
13
- };
14
- },
15
10
  addAttributes() {
16
11
  return ['id', 'title', 'sizes', 'mimeType', 'url', 'filename', 'legend'].reduce((acc, key) => {
17
12
  acc[key] = { default: null };
18
13
  return acc;
19
- }, {});
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 } });
20
18
  },
21
- //@ts-expect-error annoying
22
19
  addCommands() {
23
20
  return {
24
- insertMedia: (attributes = {}) =>
25
- //@ts-expect-error annoying
26
- ({ commands }) => {
21
+ insertUpload: (attributes = {}) => ({ commands }) => {
27
22
  return commands.insertContent({
28
23
  type: this.name,
29
24
  attrs: attributes