includio-cms 0.0.27 → 0.0.29

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.
@@ -7,7 +7,7 @@
7
7
  let { children }: { children: Snippet } = $props();
8
8
  </script>
9
9
 
10
- <ModeWatcher />
10
+ <ModeWatcher />
11
11
  <Toaster />
12
12
 
13
13
  <svelte:boundary>
@@ -74,7 +74,9 @@
74
74
  ...$value.slice(index + 1)
75
75
  ];
76
76
 
77
- openAndCloseOthers(index + 1);
77
+ tick().then(() => {
78
+ openAndCloseOthers(index + 1);
79
+ });
78
80
  }
79
81
 
80
82
  function moveItemUp(index: number) {
@@ -82,7 +84,10 @@
82
84
  const newValue = [...$value];
83
85
  [newValue[index - 1], newValue[index]] = [newValue[index], newValue[index - 1]];
84
86
  $value = newValue;
85
- openAndCloseOthers(index - 1);
87
+
88
+ tick().then(() => {
89
+ openAndCloseOthers(index - 1);
90
+ });
86
91
  }
87
92
 
88
93
  function moveItemDown(index: number) {
@@ -90,7 +95,10 @@
90
95
  const newValue = [...$value];
91
96
  [newValue[index], newValue[index + 1]] = [newValue[index + 1], newValue[index]];
92
97
  $value = newValue;
93
- openAndCloseOthers(index + 1);
98
+
99
+ tick().then(() => {
100
+ openAndCloseOthers(index + 1);
101
+ });
94
102
  }
95
103
 
96
104
  function removeItem(index: number) {
@@ -15,6 +15,10 @@
15
15
  import SeoField from './seo-field.svelte';
16
16
  import UrlField from './url-field.svelte';
17
17
  import RelationField from './relation-field.svelte';
18
+ import { urlFieldDataSchema } from '../../../schemas/field/url.js';
19
+ import { getAtPath, setAtPath } from '../../utils/objectPath.js';
20
+ import { onMount } from 'svelte';
21
+ import UrlFieldWrapper from './url-field-wrapper.svelte';
18
22
 
19
23
  type Props = {
20
24
  objectFieldType?: 'default' | 'inline';
@@ -87,7 +91,7 @@
87
91
  {:else if field.type === 'seo'}
88
92
  <SeoField {field} {form} {path} {...props} />
89
93
  {:else if field.type === 'url'}
90
- <UrlField {field} {form} {path} {...props} />
94
+ <UrlFieldWrapper {field} {form} {path} {...props} />
91
95
  {:else if field.type === 'relation'}
92
96
  <RelationField {field} {form} {path} {...props} />
93
97
  {:else}
@@ -23,6 +23,12 @@
23
23
  let { field, form, path, ...props }: Props = $props();
24
24
 
25
25
  const { value } = formFieldProxy(form, path) satisfies FormFieldProxy<string | undefined>;
26
+
27
+ onMount(() => {
28
+ if ($value === undefined) {
29
+ $value = '';
30
+ }
31
+ });
26
32
  </script>
27
33
 
28
34
  {#if field.multiline}
@@ -0,0 +1,53 @@
1
+ <script lang="ts" module>
2
+ type T = Record<string, unknown>;
3
+ </script>
4
+
5
+ <script lang="ts" generics="T extends Record<string, unknown>">
6
+ import { urlFieldDataSchema } from '../../../schemas/field/url.js';
7
+ import type { UrlField, UrlFieldData } from '../../../types/fields.js';
8
+ import { onMount } from 'svelte';
9
+ import {
10
+ formFieldProxy,
11
+ type FormFieldProxy,
12
+ type FormPathLeaves,
13
+ type SuperForm
14
+ } from 'sveltekit-superforms';
15
+ import UrlFieldComponent from './url-field.svelte';
16
+
17
+ type Props = {
18
+ field: UrlField;
19
+ form: SuperForm<T>;
20
+ path: FormPathLeaves<T, UrlFieldData | undefined>;
21
+ };
22
+
23
+ let { field, form, path, ...props }: Props = $props();
24
+
25
+ const { value } = formFieldProxy(form, path) satisfies FormFieldProxy<UrlFieldData | undefined>;
26
+
27
+ onMount(() => {
28
+ if (!$value) {
29
+ $value = { id: '', url: {} };
30
+ }
31
+
32
+ if (urlFieldDataSchema.safeParse($value).success) {
33
+ fieldValid = true;
34
+ } else {
35
+ console.warn(
36
+ `Warning: The value for the URL field "${field.slug}" is invalid. Expected UrlFieldData structure.`
37
+ );
38
+ fieldValid = false;
39
+ }
40
+ });
41
+
42
+ let fieldValid = $state(false);
43
+ </script>
44
+
45
+ <svelte:boundary>
46
+ {#snippet pending()}
47
+ Loading...
48
+ {/snippet}
49
+
50
+ {#if fieldValid}
51
+ <UrlFieldComponent {form} {field} path={path as FormPathLeaves<T, UrlFieldData>} {...props} />
52
+ {/if}
53
+ </svelte:boundary>
@@ -0,0 +1,30 @@
1
+ import type { UrlField, UrlFieldData } from '../../../types/fields.js';
2
+ import { type FormPathLeaves, type SuperForm } from 'sveltekit-superforms';
3
+ declare function $$render<T extends Record<string, unknown>>(): {
4
+ props: {
5
+ field: UrlField;
6
+ form: SuperForm<T>;
7
+ path: FormPathLeaves<T, UrlFieldData | undefined>;
8
+ };
9
+ exports: {};
10
+ bindings: "";
11
+ slots: {};
12
+ events: {};
13
+ };
14
+ declare class __sveltets_Render<T extends Record<string, unknown>> {
15
+ props(): ReturnType<typeof $$render<T>>['props'];
16
+ events(): ReturnType<typeof $$render<T>>['events'];
17
+ slots(): ReturnType<typeof $$render<T>>['slots'];
18
+ bindings(): "";
19
+ exports(): {};
20
+ }
21
+ interface $$IsomorphicComponent {
22
+ new <T extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
23
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
24
+ } & ReturnType<__sveltets_Render<T>['exports']>;
25
+ <T extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
26
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
27
+ }
28
+ declare const UrlFieldWrapper: $$IsomorphicComponent;
29
+ type UrlFieldWrapper<T extends Record<string, unknown>> = InstanceType<typeof UrlFieldWrapper<T>>;
30
+ export default UrlFieldWrapper;
@@ -22,7 +22,7 @@
22
22
  type Props = {
23
23
  field: UrlField;
24
24
  form: SuperForm<T>;
25
- path: FormPathLeaves<T, UrlFieldData | undefined>;
25
+ path: FormPathLeaves<T, UrlFieldData>;
26
26
  };
27
27
 
28
28
  const textField: TextField = {
@@ -32,103 +32,104 @@
32
32
 
33
33
  let { field, form, path, ...props }: Props = $props();
34
34
 
35
- const { value } = formFieldProxy(form, path) satisfies FormFieldProxy<UrlFieldData | undefined>;
36
-
37
- let linkedEntry: Promise<(Entry & { data: { seo?: SeoFieldData } }) | null> | null = $derived(
38
- $value !== undefined && $value.id && $value.id.length > 0
39
- ? remotes.getEntry({
40
- data: { id: $value.id }
41
- })
42
- : null
43
- );
44
-
45
- let autocompleteSuggestions = $derived.by(async () => {
46
- if ($value !== undefined && !$value.id) {
47
- let suggestions: (Entry & { data: { seo?: SeoFieldData } })[] = [];
48
-
49
- for (let i = 0; i < Object.values($value.url).length; i++) {
50
- let url = Object.values($value.url)[i];
51
-
52
- if (url.length > 2) {
53
- let results = (await remotes.getEntries({
54
- data: {
55
- dataLike: {
56
- seo: {
57
- slug: url
58
- }
35
+ const { value } = formFieldProxy(form, path) satisfies FormFieldProxy<UrlFieldData>;
36
+
37
+ $effect(() => {
38
+ if ($value.id && $value.id.length > 0) {
39
+ fetchLinkedEntry($value.id).then((entry) => {
40
+ linkedEntry = entry;
41
+ });
42
+ }
43
+ if ($value.url && Object.values($value.url).some((url) => url.length > 2)) {
44
+ fetchSuggestions($value.url).then((suggestions) => {
45
+ autocompleteSuggestions = suggestions;
46
+ });
47
+ }
48
+ });
49
+
50
+ async function fetchLinkedEntry(id: string | undefined) {
51
+ if (!id) return null;
52
+
53
+ return remotes.getEntry({
54
+ data: { id }
55
+ });
56
+ }
57
+
58
+ let linkedEntry = $state<(Entry & { data: { seo?: SeoFieldData } }) | null>(null);
59
+
60
+ async function fetchSuggestions(currUrl: Record<string, string>) {
61
+ let suggestions: (Entry & { data: { seo?: SeoFieldData } })[] = [];
62
+
63
+ for (let i = 0; i < Object.values(currUrl).length; i++) {
64
+ let url = Object.values(currUrl)[i];
65
+
66
+ if (url.length > 2) {
67
+ let results = (await remotes.getEntries({
68
+ data: {
69
+ dataLike: {
70
+ seo: {
71
+ slug: url
59
72
  }
60
73
  }
61
- })) as (Entry & { data: { seo?: SeoFieldData } })[];
74
+ }
75
+ })) as (Entry & { data: { seo?: SeoFieldData } })[];
62
76
 
63
- suggestions = [...suggestions, ...results];
77
+ suggestions = [...suggestions, ...results];
64
78
 
65
- // Remove duplicates
66
- suggestions = suggestions.filter(
67
- (entry, index, self) => index === self.findIndex((e) => e.id === entry.id)
68
- );
79
+ // Remove duplicates
80
+ suggestions = suggestions.filter(
81
+ (entry, index, self) => index === self.findIndex((e) => e.id === entry.id)
82
+ );
69
83
 
70
- // Limit to 5 suggestions
71
- suggestions = suggestions.slice(0, 5);
72
- }
84
+ // Limit to 5 suggestions
85
+ suggestions = suggestions.slice(0, 5);
73
86
  }
74
-
75
- return suggestions;
76
87
  }
77
- });
88
+
89
+ return suggestions;
90
+ }
91
+
92
+ let autocompleteSuggestions = $state<(Entry & { data: { seo?: SeoFieldData } })[]>([]);
78
93
  </script>
79
94
 
80
- {#if $value === undefined || !$value.id}
95
+ <div class={$value.id && $value.id.length > 0 ? 'hidden' : ''}>
81
96
  <TextFieldWrapper
82
97
  field={textField}
83
98
  {form}
84
99
  path={joinPath(path, textField.slug) as FormPathLeaves<T, string | undefined>}
85
100
  {...props}
86
101
  />
102
+ </div>
103
+
104
+ {#if linkedEntry}
105
+ <Button
106
+ variant="outline"
107
+ onclick={() => {
108
+ $value.id = '';
109
+ }}
110
+ >
111
+ Linked to: {linkedEntry.data.seo?.title || 'No title'} ({linkedEntry.data.seo?.slug})
112
+ </Button>
87
113
  {/if}
88
- {#if linkedEntry !== undefined}
89
- {#if linkedEntry}
90
- {#await linkedEntry then entry}
91
- {#if entry}
92
- <Button
114
+
115
+ {#if autocompleteSuggestions.length > 0}
116
+ <ul>
117
+ {#each autocompleteSuggestions as suggestion}
118
+ <li>
119
+ <Item.Root
93
120
  variant="outline"
121
+ size="sm"
94
122
  onclick={() => {
95
- if ($value !== undefined) {
96
- $value.id = '';
97
- }
123
+ $value.id = suggestion.id;
98
124
  }}
99
125
  >
100
- Linked to: {entry.data.seo?.title || 'No title'} ({entry.data.seo?.slug})
101
- </Button>
102
- {/if}
103
- {/await}
104
- {/if}
105
- {/if}
106
-
107
- {#if autocompleteSuggestions}
108
- {#await autocompleteSuggestions then suggestions}
109
- {#if suggestions && suggestions.length > 0}
110
- <ul>
111
- {#each suggestions as suggestion}
112
- <li>
113
- <Item.Root
114
- variant="outline"
115
- size="sm"
116
- onclick={() => {
117
- if ($value !== undefined) {
118
- $value.id = suggestion.id;
119
- }
120
- }}
126
+ <Item.Content>
127
+ <Item.Title
128
+ >{suggestion.data.seo?.title || 'No title'} ({suggestion.data.seo?.slug})</Item.Title
121
129
  >
122
- <Item.Content>
123
- <Item.Title
124
- >{suggestion.data.seo?.title || 'No title'} ({suggestion.data.seo
125
- ?.slug})</Item.Title
126
- >
127
- </Item.Content>
128
- </Item.Root>
129
- </li>
130
- {/each}
131
- </ul>
132
- {/if}
133
- {/await}
130
+ </Item.Content>
131
+ </Item.Root>
132
+ </li>
133
+ {/each}
134
+ </ul>
134
135
  {/if}
@@ -4,7 +4,7 @@ declare function $$render<T extends Record<string, unknown>>(): {
4
4
  props: {
5
5
  field: UrlField;
6
6
  form: SuperForm<T>;
7
- path: FormPathLeaves<T, UrlFieldData | undefined>;
7
+ path: FormPathLeaves<T, UrlFieldData>;
8
8
  };
9
9
  exports: {};
10
10
  bindings: "";
@@ -66,6 +66,7 @@ export async function resolveUrlFields(data, fields) {
66
66
  const parsedVal = urlFieldDataSchema.safeParse(val);
67
67
  if (parsedVal.success) {
68
68
  result[field.slug] = parsedVal.data.url;
69
+ break;
69
70
  }
70
71
  break;
71
72
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "includio-cms",
3
- "version": "0.0.27",
3
+ "version": "0.0.29",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",
@@ -174,6 +174,7 @@
174
174
  "path-to-regexp": "^8.2.0",
175
175
  "postgres": "^3.4.5",
176
176
  "readline": "^1.3.0",
177
+ "runed": "^0.35.1",
177
178
  "sharp": "^0.34.2",
178
179
  "slugify": "^1.6.6",
179
180
  "svelte-sonner": "^1.0.5",