rimecms 0.27.2 → 0.27.4

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.
@@ -1,6 +1,6 @@
1
1
  import type { BuiltCollection } from '../../config/types.js';
2
2
  import type { RequestEvent } from '@sveltejs/kit';
3
- type DeleteArgs = {
3
+ type DuplicateArgs = {
4
4
  id: string;
5
5
  config: BuiltCollection;
6
6
  event: RequestEvent & {
@@ -8,5 +8,5 @@ type DeleteArgs = {
8
8
  };
9
9
  isSystemOperation?: boolean;
10
10
  };
11
- export declare const duplicate: (args: DeleteArgs) => Promise<string>;
11
+ export declare const duplicate: (args: DuplicateArgs) => Promise<string>;
12
12
  export {};
@@ -1,6 +1,8 @@
1
1
  import { VERSIONS_STATUS } from '../../constant.js';
2
2
  import { buildConfigMap } from '../../operations/configMap/index.js';
3
3
  import { isBlocksFieldRaw } from '../../../fields/blocks/index.js';
4
+ import { isJSONContent } from '../../../fields/rich-text';
5
+ import { richTextJSONToText } from '../../../fields/rich-text/index.js';
4
6
  import { isTreeFieldRaw } from '../../../fields/tree/index.js';
5
7
  import { getValueAtPath, isObjectLiteral, matchStructure, omitId, setValueAtPath } from '../../../util/object.js';
6
8
  // If block is localized should not keep its id so it created a new one
@@ -13,8 +15,13 @@ export const duplicate = async (args) => {
13
15
  * on the given document
14
16
  */
15
17
  function setCopyTitle(doc) {
16
- const title = getValueAtPath(config.asTitle, doc);
17
- const data = setValueAtPath(config.asTitle, doc, title + ' (copy)');
18
+ const getTitle = () => {
19
+ const title = getValueAtPath(config.asTitle, doc);
20
+ return isJSONContent(title)
21
+ ? richTextJSONToText(title) + ' (copy)'
22
+ : title + ' (copy)';
23
+ };
24
+ const data = setValueAtPath(config.asTitle, doc, getTitle());
18
25
  return data;
19
26
  }
20
27
  /**
@@ -1,5 +1,15 @@
1
1
  import type { AreaSlug, CollectionSlug, Config, PrototypeSlug } from '../../types.js';
2
2
  import type { BuildConfig } from './server/build-config.server.js';
3
+ /**
4
+ * Object passed to the locals.rime to access the configuration in the server context
5
+ * it is created once on server start and can be used in any server context (load, actions, hooks, etc) via `event.locals.rime.config`
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * // In +page.server.ts
10
+ * const config = event.locals.rime.config.raw;
11
+ * ```
12
+ */
3
13
  export declare function createConfigContext<const C extends Config>(config: BuildConfig<C>): {
4
14
  /**
5
15
  * Gets raw config object
@@ -1,5 +1,15 @@
1
1
  import { ensureMedias } from '../ensure.server.js';
2
2
  import { RimeError } from '../errors/index.js';
3
+ /**
4
+ * Object passed to the locals.rime to access the configuration in the server context
5
+ * it is created once on server start and can be used in any server context (load, actions, hooks, etc) via `event.locals.rime.config`
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * // In +page.server.ts
10
+ * const config = event.locals.rime.config.raw;
11
+ * ```
12
+ */
3
13
  export function createConfigContext(config) {
4
14
  //
5
15
  ensureMedias(config);
@@ -17,6 +17,7 @@ declare namespace _default {
17
17
  export let delete_dialog_title: string;
18
18
  export let delete_dialog_text: string;
19
19
  export let duplicate: string;
20
+ export let duplicate_success: string;
20
21
  export let doc_deleted: string;
21
22
  export let doc_created: string;
22
23
  export let doc_updated: string;
@@ -17,6 +17,7 @@ export default {
17
17
  delete_dialog_title: 'Delete $1',
18
18
  delete_dialog_text: 'You are about to delete $1, press delete to confirm the operation.',
19
19
  duplicate: 'Duplicate',
20
+ duplicate_success: 'Duplicated successfully',
20
21
  doc_deleted: 'Document deleted',
21
22
  doc_created: 'Document created',
22
23
  doc_updated: 'Document updated',
@@ -16,6 +16,7 @@ declare namespace _default {
16
16
  export let delete_dialog_title: string;
17
17
  export let delete_dialog_text: string;
18
18
  export let duplicate: string;
19
+ export let duplicate_success: string;
19
20
  export let doc_deleted: string;
20
21
  export let doc_created: string;
21
22
  export let doc_updated: string;
@@ -17,6 +17,7 @@ export default {
17
17
  delete_dialog_text:
18
18
  "Vous êtes sur le point de supprimer $1, appuyez sur supprimer pour confirmer l'opération.",
19
19
  duplicate: 'Dupliquer',
20
+ duplicate_success: 'Dupliqué avec succès',
20
21
  doc_deleted: 'Document supprimé',
21
22
  doc_created: 'Document enregistré',
22
23
  doc_updated: 'Document mis à jour',
@@ -4,6 +4,7 @@
4
4
  import { Editor, type JSONContent } from '@tiptap/core';
5
5
  import { onMount } from 'svelte';
6
6
  import { buildEditorConfig } from '../core/build-editor-config.js';
7
+ import { defaultFeatures } from '../core/features/index.js';
7
8
  import type { RichTextFeature } from '../core/types';
8
9
  import { hasSuggestion } from '../util.js';
9
10
  import EditorBubbleMenu from './bubble-menu/bubble-menu.svelte';
@@ -26,11 +27,13 @@
26
27
  setRichTextContext(path);
27
28
  });
28
29
 
29
- const withSuggestion = $derived(hasSuggestion(config.features || []));
30
+ const withSuggestion = $derived(hasSuggestion(config.features || defaultFeatures));
30
31
 
31
32
  onMount(() => {
32
33
  // Build editor configuration
33
- const richTextEditorConfig = buildEditorConfig({ features: config.features });
34
+ const richTextEditorConfig = buildEditorConfig({
35
+ features: config.features || defaultFeatures
36
+ });
34
37
 
35
38
  features = richTextEditorConfig.features;
36
39
  editor = new Editor({
@@ -36,7 +36,7 @@
36
36
  /** All editor > node */
37
37
  .ProseMirror > *:not(.ProseMirror-gapcursor) {
38
38
  background-color: hsl(var(--rz-input-bg));
39
- padding: var(--rz-size-4) var(--rz-size-3) var(--rz-size-4);
39
+ padding: var(--rz-size-3) var(--rz-size-3);
40
40
  margin-left: var(--left-spacing);
41
41
  width: calc(100% - var(--left-spacing));
42
42
  min-height: var(--rz-size-11);
@@ -1,6 +1,6 @@
1
1
  import type { RichTextEditorConfig, RichTextFeature } from './types.js';
2
2
  type BuildEditorConfigArgs = {
3
- features?: Array<RichTextFeature>;
3
+ features: Array<RichTextFeature>;
4
4
  standAlone?: boolean;
5
5
  };
6
6
  /**
@@ -12,14 +12,12 @@ import { getLocaleContext, LOCALE_CTX } from '../../../panel/context/locale.svel
12
12
  import { getUserContext, USER_CTX } from '../../../panel/context/user.svelte.js';
13
13
  import { hasSuggestion } from '../util.js';
14
14
  import { CurrentNodeAttribute } from './extensions/current-node/current-node.js';
15
- import { defaultFeatures } from './features/index.js';
16
15
  import { ParagraphFeature } from './features/paragraph.js';
17
16
  /**
18
17
  * Builds a rich text editor configuration based on the provided features
19
18
  */
20
19
  export function buildEditorConfig(args) {
21
- const { features: incommingFeatures = [] } = args;
22
- const features = incommingFeatures.length === 0 ? defaultFeatures : incommingFeatures;
20
+ const { features } = args;
23
21
  const withSuggestion = hasSuggestion(features);
24
22
  // Add mandatory paragraph feature if not provided
25
23
  const hasParagraph = features
@@ -30,6 +30,14 @@ export declare class RichTextFieldBuilder extends FormFieldBuilder<RichTextField
30
30
  defaultValue(value: RichTextContent | DefaultValueFn<RichTextContent>): this;
31
31
  }
32
32
  export declare const richText: (name: string) => RichTextFieldBuilder;
33
+ /**
34
+ * Type guard to check if a value is a JSONContent object.
35
+ *
36
+ * @example
37
+ * isJSONContent({ type: 'paragraph', content: [] }) // true
38
+ * isJSONContent('Just a string') // false
39
+ */
40
+ export declare const isJSONContent: (value: any) => value is JSONContent;
33
41
  /**
34
42
  * Converts rich text JSON content to plain text.
35
43
  * Extracts text content from a TipTap/ProseMirror JSON structure.
@@ -101,6 +101,16 @@ export class RichTextFieldBuilder extends FormFieldBuilder {
101
101
  }
102
102
  }
103
103
  export const richText = (name) => new RichTextFieldBuilder(name);
104
+ /**
105
+ * Type guard to check if a value is a JSONContent object.
106
+ *
107
+ * @example
108
+ * isJSONContent({ type: 'paragraph', content: [] }) // true
109
+ * isJSONContent('Just a string') // false
110
+ */
111
+ export const isJSONContent = (value) => {
112
+ return typeof value === 'object' && value !== null && 'content' in value;
113
+ };
104
114
  /**
105
115
  * Converts rich text JSON content to plain text.
106
116
  * Extracts text content from a TipTap/ProseMirror JSON structure.
@@ -86,6 +86,6 @@
86
86
  }
87
87
 
88
88
  .rz-panel-root__right--navCollapsed {
89
- margin-left: var(--rz-size-20);
89
+ margin-left: var(--rz-size-14);
90
90
  }
91
91
  </style>
@@ -73,6 +73,7 @@
73
73
  }
74
74
 
75
75
  async function duplicate() {
76
+ form.reset();
76
77
  const url = `${apiUrl(form.config.kebab, form.values.id)}/duplicate`;
77
78
 
78
79
  const [error, success] = await trycatchFetch(url, {
@@ -1,5 +1,4 @@
1
1
  <script lang="ts">
2
- import { page } from '$app/state';
3
2
  import { getConfigContext } from '../../../context/config.svelte.js';
4
3
  import type { Route } from '../../../types';
5
4
  import { panelUrl } from '../../../util/url.js';
@@ -43,25 +42,23 @@
43
42
 
44
43
  <div class="rz-nav__body">
45
44
  <ScrollArea>
46
- {#key page.url}
47
- <nav class="rz-nav__nav">
48
- {#each Object.entries(routesGroups) as [groupName, routes], index (index)}
49
- {#if groupName !== 'none'}
50
- {@const icon = getGroupIcon(groupName)}
51
- <NavGroup name={groupName} {icon} navCollapsed={isCollapsed}>
52
- {#each routes as route (route.url)}
53
- <NavItem href={route.url} {isCollapsed} {route} />
54
- {/each}
55
- </NavGroup>
56
- {/if}
57
- {/each}
58
- {#each routesGroups.none as route (route.url)}
59
- <div class="rz-nav__group-none">
60
- <NavItem href={route.url} {isCollapsed} {route} />
61
- </div>
62
- {/each}
63
- </nav>
64
- {/key}
45
+ <nav class="rz-nav__nav">
46
+ {#each Object.entries(routesGroups) as [groupName, routes], index (index)}
47
+ {#if groupName !== 'none'}
48
+ {@const icon = getGroupIcon(groupName)}
49
+ <NavGroup name={groupName} {icon} navCollapsed={isCollapsed}>
50
+ {#each routes as route (route.url)}
51
+ <NavItem href={route.url} {isCollapsed} {route} />
52
+ {/each}
53
+ </NavGroup>
54
+ {/if}
55
+ {/each}
56
+ {#each routesGroups.none as route (route.url)}
57
+ <div class="rz-nav__group-none">
58
+ <NavItem href={route.url} {isCollapsed} {route} />
59
+ </div>
60
+ {/each}
61
+ </nav>
65
62
  </ScrollArea>
66
63
 
67
64
  <div class="rz-nav__user">
@@ -119,7 +116,7 @@
119
116
  }
120
117
  }
121
118
  .rz-nav--collapsed {
122
- width: var(--rz-size-20);
119
+ width: var(--rz-size-14);
123
120
  padding: var(--rz-size-3);
124
121
  :global(.rz-button-nav) {
125
122
  justify-content: start;
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { ChevronDown, ChevronUp, type IconProps } from '@lucide/svelte';
2
+ import { ChevronUp, type IconProps } from '@lucide/svelte';
3
3
  import type { Component, Snippet } from 'svelte';
4
4
 
5
5
  type Props = {
@@ -35,18 +35,15 @@
35
35
  {/if}
36
36
  {name}
37
37
  </span>
38
- {#if groupCollapsed}
39
- <ChevronDown size="12" />
40
- {:else}
41
- <ChevronUp size="12" />
42
- {/if}
38
+ <ChevronUp class="rz-nav-group__chevron" size="12" />
43
39
  </button>
44
40
  {/if}
45
- {#if !groupCollapsed || navCollapsed}
46
- <div class="rz-nav-group__content">
41
+
42
+ <div class="rz-nav-group__content">
43
+ <div class="rz-nav-group__content-inner">
47
44
  {@render children()}
48
45
  </div>
49
- {/if}
46
+ </div>
50
47
  </div>
51
48
 
52
49
  <style global>
@@ -58,14 +55,27 @@
58
55
  margin-bottom: var(--rz-size-2);
59
56
  background-color: var(--rz-nav-button-bg);
60
57
  border-radius: var(--rz-radius-lg);
58
+
59
+ :global {
60
+ .rz-nav-group__chevron {
61
+ transition: transform 0.3s var(--ease-in-out-quart);
62
+ }
63
+ }
61
64
  }
62
65
 
63
66
  .rz-nav-group__content {
64
67
  display: grid;
68
+ grid-template-rows: 1fr;
65
69
  padding: 0 var(--rz-size-4);
66
70
  background-color: var(--rz-nav-group-bg);
67
71
  border-bottom-left-radius: var(--rz-radius-lg);
68
72
  border-bottom-right-radius: var(--rz-radius-lg);
73
+ transition: grid-template-rows 0.3s var(--ease-in-out-quart);
74
+ }
75
+
76
+ .rz-nav-group__content-inner {
77
+ overflow: hidden;
78
+ display: grid;
69
79
  }
70
80
 
71
81
  .rz-nav-group__trigger {
@@ -79,6 +89,7 @@
79
89
  justify-content: space-between;
80
90
  text-align: left;
81
91
  border-bottom: 1px solid var(--rz-nav-group-border-color);
92
+ transition: border-color 0.3s var(--ease-in-out-quart);
82
93
 
83
94
  span {
84
95
  display: flex;
@@ -87,17 +98,26 @@
87
98
  }
88
99
  }
89
100
 
90
- .rz-nav-group--collapsed {
101
+ .rz-nav-group.rz-nav-group--collapsed:not(.rz-nav-group--nav-collapsed) {
91
102
  .rz-nav-group__trigger {
92
103
  border-color: transparent;
93
104
  }
105
+
106
+ .rz-nav-group__content {
107
+ grid-template-rows: 0fr;
108
+ }
109
+ :global {
110
+ .rz-nav-group__chevron {
111
+ transform: rotate(180deg);
112
+ }
113
+ }
94
114
  }
95
115
 
96
116
  .rz-nav-group--nav-collapsed {
97
- /*padding-top: var(--rz-size-2);*/
117
+ background-color: transparent;
98
118
  .rz-nav-group__content {
99
119
  background-color: transparent;
100
- padding: 0 var(--rz-size-5);
120
+ padding: 0 var(--rz-size-2);
101
121
  }
102
122
  }
103
123
  </style>
@@ -19,7 +19,7 @@
19
19
  typeof route.icon === 'function' ? route.icon : config.raw.icons[route.icon] || File
20
20
  );
21
21
 
22
- let pathname = page.url.pathname;
22
+ let pathname = $derived(page.url.pathname);
23
23
 
24
24
  let active = $derived.by(() => {
25
25
  const routePathname = new URL(route.url).pathname;
@@ -25,8 +25,13 @@
25
25
  {@render children?.()}
26
26
  </a>
27
27
 
28
- <style>
29
- .rz-button-nav {
28
+ <style>/**************************************/
29
+
30
+ /* Font */
31
+
32
+ /**************************************/
33
+
34
+ .rz-button-nav {
30
35
  position: relative;
31
36
  display: inline-flex;
32
37
  align-items: center;
@@ -46,7 +51,7 @@
46
51
  }
47
52
  }
48
53
 
49
- .rz-button-nav--active {
54
+ .rz-button-nav--active {
50
55
  text-decoration: underline;
51
56
  position: relative;
52
57
  }
@@ -69,6 +69,7 @@ export declare function setDocumentFormContext<T extends WithOptional<GenericDoc
69
69
  none?: string | undefined;
70
70
  };
71
71
  readonly isLive: boolean;
72
+ reset(): void;
72
73
  setFocusedField(path: string): void;
73
74
  };
74
75
  export declare function getDocumentFormContext<T extends WithOptional<GenericDoc, 'id'> = WithOptional<GenericDoc, 'id'>>(key?: string): {
@@ -138,6 +139,7 @@ export declare function getDocumentFormContext<T extends WithOptional<GenericDoc
138
139
  none?: string | undefined;
139
140
  };
140
141
  readonly isLive: boolean;
142
+ reset(): void;
141
143
  setFocusedField(path: string): void;
142
144
  };
143
145
  export type DocumentFormContext<T extends WithOptional<GenericDoc, 'id'> = WithOptional<GenericDoc, 'id'>> = ReturnType<typeof setDocumentFormContext<T>>;
@@ -5,7 +5,7 @@ import { compileDocumentConfig } from '../../core/config/shared/compile.js';
5
5
  import { PARAMS, VERSIONS_STATUS } from '../../core/constant.js';
6
6
  import { getFieldConfigByPath } from '../../core/fields/util.js';
7
7
  import { buildConfigMap } from '../../core/operations/configMap/index.js';
8
- import { richTextJSONToText } from '../../fields/rich-text/index.js';
8
+ import { isJSONContent, richTextJSONToText } from '../../fields/rich-text/index.js';
9
9
  import { normalizeFieldPath } from '../../util/doc.js';
10
10
  import { random } from '../../util/index.js';
11
11
  import { isObjectLiteral, omit } from '../../util/object.js';
@@ -60,7 +60,7 @@ function createDocumentFormState({ initial, config, readOnly, key, onNestedDocum
60
60
  else {
61
61
  function computeTitleFromValue(value) {
62
62
  // Handle rich text value
63
- if (isObjectLiteral(value) && 'content' in value) {
63
+ if (isJSONContent(value)) {
64
64
  return richTextJSONToText(value);
65
65
  }
66
66
  if (typeof value === 'string') {
@@ -678,6 +678,9 @@ function createDocumentFormState({ initial, config, readOnly, key, onNestedDocum
678
678
  get isLive() {
679
679
  return isLiveEdit;
680
680
  },
681
+ reset() {
682
+ doc = initialDoc;
683
+ },
681
684
  setFocusedField(path) {
682
685
  if (isLiveEdit && onFieldFocus) {
683
686
  onFieldFocus(path);
@@ -1,4 +1,5 @@
1
1
  @import '@bienbien/css-reset';
2
+ @import '@bienbien/css-easing';
2
3
  @import './size.css';
3
4
  @import './font.css';
4
5
  @import './radius.css';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rimecms",
3
- "version": "0.27.2",
3
+ "version": "0.27.4",
4
4
  "homepage": "https://github.com/bienbiendev/rime",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -175,6 +175,7 @@
175
175
  "dependencies": {
176
176
  "@babel/generator": "^7.28.3",
177
177
  "@babel/types": "^7.28.4",
178
+ "@bienbien/css-easing": "^0.1.1",
178
179
  "@bienbien/css-reset": "^0.1.2",
179
180
  "@floating-ui/dom": "^1.7.4",
180
181
  "@internationalized/date": "^3.8.2",
@@ -1,12 +0,0 @@
1
- <span aria-hidden="true" class="button-nav__caret"></span>
2
-
3
- <style>
4
- .button-nav__caret {
5
- background-color: hsl(var(--rz-color-spot));
6
- position: absolute;
7
- left: calc(-1 * var(--rz-size-4));
8
- bottom: 0;
9
- top: 0;
10
- width: var(--rz-size-1);
11
- }
12
- </style>
@@ -1,26 +0,0 @@
1
- export default NavItemButtonCaret;
2
- type NavItemButtonCaret = SvelteComponent<{
3
- [x: string]: never;
4
- }, {
5
- [evt: string]: CustomEvent<any>;
6
- }, {}> & {
7
- $$bindings?: string | undefined;
8
- };
9
- declare const NavItemButtonCaret: $$__sveltets_2_IsomorphicComponent<{
10
- [x: string]: never;
11
- }, {
12
- [evt: string]: CustomEvent<any>;
13
- }, {}, {}, string>;
14
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
15
- new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
16
- $$bindings?: Bindings;
17
- } & Exports;
18
- (internal: unknown, props: {
19
- $$events?: Events;
20
- $$slots?: Slots;
21
- }): Exports & {
22
- $set?: any;
23
- $on?: any;
24
- };
25
- z_$$bindings?: Bindings;
26
- }