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.
- package/dist/admin/client/admin/admin-layout.svelte +1 -1
- package/dist/admin/components/fields/array-field.svelte +11 -3
- package/dist/admin/components/fields/field-renderer.svelte +5 -1
- package/dist/admin/components/fields/text-field.svelte +6 -0
- package/dist/admin/components/fields/url-field-wrapper.svelte +53 -0
- package/dist/admin/components/fields/url-field-wrapper.svelte.d.ts +30 -0
- package/dist/admin/components/fields/url-field.svelte +80 -79
- package/dist/admin/components/fields/url-field.svelte.d.ts +1 -1
- package/dist/core/server/fields/resolveUrlFields.js +1 -0
- package/package.json +2 -1
|
@@ -74,7 +74,9 @@
|
|
|
74
74
|
...$value.slice(index + 1)
|
|
75
75
|
];
|
|
76
76
|
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
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
|
|
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
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
}
|
|
74
|
+
}
|
|
75
|
+
})) as (Entry & { data: { seo?: SeoFieldData } })[];
|
|
62
76
|
|
|
63
|
-
|
|
77
|
+
suggestions = [...suggestions, ...results];
|
|
64
78
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
// Remove duplicates
|
|
80
|
+
suggestions = suggestions.filter(
|
|
81
|
+
(entry, index, self) => index === self.findIndex((e) => e.id === entry.id)
|
|
82
|
+
);
|
|
69
83
|
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
{
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
96
|
-
$value.id = '';
|
|
97
|
-
}
|
|
123
|
+
$value.id = suggestion.id;
|
|
98
124
|
}}
|
|
99
125
|
>
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "includio-cms",
|
|
3
|
-
"version": "0.0.
|
|
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",
|