includio-cms 0.1.4 → 0.5.0
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/CHANGELOG.md +68 -0
- package/ROADMAP.md +18 -13
- package/dist/admin/api/accept-invite.js +1 -5
- package/dist/admin/api/invite.js +7 -16
- package/dist/admin/client/account/account-page.svelte +20 -50
- package/dist/admin/client/account/lang.d.ts +15 -23
- package/dist/admin/client/account/lang.js +51 -67
- package/dist/admin/client/account/preferences-section.svelte +26 -84
- package/dist/admin/client/account/profile-section.svelte +60 -40
- package/dist/admin/client/account/schema.d.ts +11 -3
- package/dist/admin/client/account/schema.js +25 -16
- package/dist/admin/client/account/security-section.svelte +139 -105
- package/dist/admin/client/account/sessions-section.svelte +35 -34
- package/dist/admin/client/admin/admin-after-login-layout-content.svelte +3 -5
- package/dist/admin/client/admin/admin-layout.svelte +3 -2
- package/dist/admin/client/admin/admin-preloader.svelte +36 -0
- package/dist/admin/client/admin/admin-preloader.svelte.d.ts +18 -0
- package/dist/admin/client/admin/dashboard-page.svelte +55 -41
- package/dist/admin/client/collection/a11y-score-cell.svelte +45 -0
- package/dist/admin/client/collection/a11y-score-cell.svelte.d.ts +6 -0
- package/dist/admin/client/collection/bulk-actions-bar.svelte +83 -0
- package/dist/admin/client/collection/bulk-actions-bar.svelte.d.ts +9 -0
- package/dist/admin/client/collection/collection-entries.svelte +255 -260
- package/dist/admin/client/collection/collection-view.svelte.d.ts +4 -3
- package/dist/admin/client/collection/collection-view.svelte.js +9 -5
- package/dist/admin/client/collection/collection.svelte +22 -12
- package/dist/admin/client/collection/data-table.svelte +50 -39
- package/dist/admin/client/collection/data-table.svelte.d.ts +1 -0
- package/dist/admin/client/collection/date-cell.svelte +7 -5
- package/dist/admin/client/collection/date-cell.svelte.d.ts +1 -1
- package/dist/admin/client/collection/empty-state.svelte +28 -0
- package/dist/admin/client/collection/empty-state.svelte.d.ts +9 -0
- package/dist/admin/client/collection/entry-link.svelte +10 -4
- package/dist/admin/client/collection/entry-link.svelte.d.ts +1 -0
- package/dist/admin/client/collection/grid-view.svelte +21 -23
- package/dist/admin/client/collection/grid-view.svelte.d.ts +1 -2
- package/dist/admin/client/collection/row-actions.svelte +60 -0
- package/dist/admin/client/collection/row-actions.svelte.d.ts +9 -0
- package/dist/admin/client/collection/status-badge.svelte +7 -8
- package/dist/admin/client/collection/table-pagination.svelte +122 -79
- package/dist/admin/client/collection/table-pagination.svelte.d.ts +1 -0
- package/dist/admin/client/collection/table-toolbar.svelte +108 -88
- package/dist/admin/client/collection/table-toolbar.svelte.d.ts +8 -9
- package/dist/admin/client/entry/entry-form.svelte +109 -1
- package/dist/admin/client/entry/entry-header.svelte +96 -37
- package/dist/admin/client/entry/entry-header.svelte.d.ts +5 -0
- package/dist/admin/client/entry/entry.svelte +171 -60
- package/dist/admin/client/entry/header/a11y-validator.d.ts +46 -0
- package/dist/admin/client/entry/header/a11y-validator.js +311 -0
- package/dist/admin/client/entry/header/publish-panel.svelte +373 -131
- package/dist/admin/client/entry/header/publish-panel.svelte.d.ts +4 -0
- package/dist/admin/client/entry/header/save-indicator.svelte +33 -23
- package/dist/admin/client/entry/header/schedule-popover.svelte +1 -1
- package/dist/admin/client/entry/header/status-badge.svelte +25 -118
- package/dist/admin/client/entry/header/version-history-sheet.svelte +314 -98
- package/dist/admin/client/form/form-submission/form-submission.svelte +271 -83
- package/dist/admin/client/form/form-submission/submission-field.svelte +12 -12
- package/dist/admin/client/form/form-submissions.svelte +421 -139
- package/dist/admin/client/form/submission-link.svelte +8 -2
- package/dist/admin/client/form/submission-link.svelte.d.ts +1 -0
- package/dist/admin/client/form/submission-status-badge.svelte +18 -4
- package/dist/admin/client/form/submission-status-badge.svelte.d.ts +1 -0
- package/dist/admin/client/login/lang.d.ts +32 -0
- package/dist/admin/client/login/lang.js +66 -2
- package/dist/admin/client/login/login-form.svelte +237 -95
- package/dist/admin/client/login/login-form.svelte.d.ts +2 -17
- package/dist/admin/client/login/login-page.svelte +34 -98
- package/dist/admin/client/login/reset-password-page.svelte +235 -0
- package/dist/admin/client/login/reset-password-page.svelte.d.ts +4 -0
- package/dist/admin/client/login/schema.d.ts +15 -0
- package/dist/admin/client/login/schema.js +21 -0
- package/dist/admin/client/users/accept-invite-page.svelte +166 -37
- package/dist/admin/client/users/create-user-dialog.svelte +15 -7
- package/dist/admin/client/users/delete-user-dialog.svelte +81 -16
- package/dist/admin/client/users/delete-user-dialog.svelte.d.ts +4 -1
- package/dist/admin/client/users/edit-user-dialog.svelte +3 -0
- package/dist/admin/client/users/invite-user-dialog.svelte +16 -3
- package/dist/admin/client/users/lang.d.ts +27 -0
- package/dist/admin/client/users/lang.js +64 -10
- package/dist/admin/client/users/pending-invitations.svelte +59 -23
- package/dist/admin/client/users/users-page.svelte +471 -72
- package/dist/admin/components/accessibility/accessibility-overview.svelte +2 -7
- package/dist/admin/components/dashboard/a11y-gauge.svelte +90 -0
- package/dist/admin/components/dashboard/a11y-gauge.svelte.d.ts +18 -0
- package/dist/admin/components/dashboard/accessibility-hub.svelte +13 -12
- package/dist/admin/components/dashboard/form-submissions-widget.svelte +71 -113
- package/dist/admin/components/dashboard/index.d.ts +4 -2
- package/dist/admin/components/dashboard/index.js +4 -2
- package/dist/admin/components/dashboard/recent-activity.svelte +53 -75
- package/dist/admin/components/dashboard/recent-entries.svelte +94 -0
- package/dist/admin/components/dashboard/recent-entries.svelte.d.ts +18 -0
- package/dist/admin/components/dashboard/stat-card.svelte +2 -2
- package/dist/admin/components/dashboard/tip-of-the-day.svelte +109 -0
- package/dist/admin/components/dashboard/tip-of-the-day.svelte.d.ts +3 -0
- package/dist/admin/components/dashboard/welcome-header.svelte +45 -0
- package/dist/admin/components/dashboard/welcome-header.svelte.d.ts +3 -0
- package/dist/admin/components/fields/{array-field.svelte → blocks-field.svelte} +4 -4
- package/dist/admin/components/fields/{array-field.svelte.d.ts → blocks-field.svelte.d.ts} +5 -5
- package/dist/admin/components/fields/content-field.svelte +27 -0
- package/dist/admin/components/fields/content-field.svelte.d.ts +31 -0
- package/dist/admin/components/fields/field-renderer.svelte +9 -7
- package/dist/admin/components/fields/image-field.svelte +2 -2
- package/dist/admin/components/fields/media-field.svelte +2 -2
- package/dist/admin/components/fields/seo-field.svelte +205 -25
- package/dist/admin/components/fields/simple-array-field.svelte +289 -0
- package/dist/admin/components/fields/simple-array-field.svelte.d.ts +30 -0
- package/dist/admin/components/fields/slug-field.svelte +3 -2
- package/dist/admin/components/fields/standalone-field-renderer.svelte +148 -0
- package/dist/admin/components/fields/standalone-field-renderer.svelte.d.ts +9 -0
- package/dist/admin/components/fields/text-field-wrapper.svelte +13 -1
- package/dist/admin/components/fields/text-field-wrapper.svelte.d.ts +2 -2
- package/dist/admin/components/fields/url-field.svelte +5 -4
- package/dist/admin/components/layout/app-sidebar.svelte +27 -24
- package/dist/admin/components/layout/lang.d.ts +6 -0
- package/dist/admin/components/layout/lang.js +13 -1
- package/dist/admin/components/layout/layout-renderer.svelte +352 -0
- package/dist/admin/components/layout/layout-renderer.svelte.d.ts +14 -0
- package/dist/admin/components/layout/nav-breadcrumbs.svelte +4 -4
- package/dist/admin/components/layout/nav-collections.svelte +65 -36
- package/dist/admin/components/layout/nav-footer.svelte +31 -0
- package/dist/admin/components/layout/nav-footer.svelte.d.ts +18 -0
- package/dist/admin/components/layout/nav-forms.svelte +55 -30
- package/dist/admin/components/layout/nav-main.svelte +14 -52
- package/dist/admin/components/layout/nav-search.svelte +4 -3
- package/dist/admin/components/layout/nav-singletons.svelte +59 -17
- package/dist/admin/components/layout/nav-singletons.svelte.d.ts +17 -8
- package/dist/admin/components/layout/site-header.svelte +74 -13
- package/dist/admin/components/media/alt-input.svelte +32 -22
- package/dist/admin/components/media/bulk-action-bar.svelte +139 -150
- package/dist/admin/components/media/file/file-details.svelte +299 -217
- package/dist/admin/components/media/file/file-miniature.svelte +54 -41
- package/dist/admin/components/media/file/file-miniature.svelte.d.ts +1 -0
- package/dist/admin/components/media/file/file-preview.svelte +1 -1
- package/dist/admin/components/media/file-upload.svelte +24 -26
- package/dist/admin/components/media/files-list.svelte +112 -40
- package/dist/admin/components/media/files-list.svelte.d.ts +2 -0
- package/dist/admin/components/media/focal-point-input.svelte +122 -26
- package/dist/admin/components/media/media-library.svelte +127 -70
- package/dist/admin/components/media/media-search.svelte +6 -6
- package/dist/admin/components/media/media-sort.svelte +3 -1
- package/dist/admin/components/media/multi-file-summary.svelte +88 -68
- package/dist/admin/components/media/tag-combobox.svelte +141 -66
- package/dist/admin/components/media/tag-combobox.svelte.d.ts +1 -0
- package/dist/admin/components/media/tag-sidebar.svelte +139 -121
- package/dist/admin/components/tiptap/FigureNodeView.svelte +144 -15
- package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +254 -0
- package/dist/admin/components/tiptap/InlineBlockNodeView.svelte.d.ts +4 -0
- package/dist/admin/components/tiptap/SlashCommandPopup.svelte +212 -0
- package/dist/admin/components/tiptap/SlashCommandPopup.svelte.d.ts +8 -0
- package/dist/admin/components/tiptap/content-editor.svelte +280 -0
- package/dist/admin/components/tiptap/content-editor.svelte.d.ts +9 -0
- package/dist/admin/components/tiptap/editor-toolbar.svelte +230 -0
- package/dist/admin/components/tiptap/editor-toolbar.svelte.d.ts +16 -0
- package/dist/admin/components/tiptap/heading-a11y-plugin.d.ts +2 -0
- package/dist/admin/components/tiptap/heading-a11y-plugin.js +67 -0
- package/dist/admin/components/tiptap/image-dialog.svelte +172 -11
- package/dist/admin/components/tiptap/inline-block-node.d.ts +19 -0
- package/dist/admin/components/tiptap/inline-block-node.js +98 -0
- package/dist/admin/components/tiptap/link-dialog.svelte +9 -4
- package/dist/admin/components/tiptap/slash-command.d.ts +17 -0
- package/dist/admin/components/tiptap/slash-command.js +181 -0
- package/dist/admin/components/tiptap/structured-content-utils.d.ts +21 -0
- package/dist/admin/components/tiptap/structured-content-utils.js +150 -0
- package/dist/admin/components/tiptap/tiptap-editor.svelte +18 -190
- package/dist/admin/email/invite-template.d.ts +8 -0
- package/dist/admin/email/invite-template.js +99 -0
- package/dist/admin/email/reset-password-template.d.ts +7 -0
- package/dist/admin/email/reset-password-template.js +96 -0
- package/dist/admin/remote/ai.remote.d.ts +1 -0
- package/dist/admin/remote/ai.remote.js +4 -1
- package/dist/admin/remote/entry.remote.d.ts +8 -0
- package/dist/admin/remote/entry.remote.js +53 -4
- package/dist/admin/remote/preview.remote.js +2 -1
- package/dist/admin/shared/password-schema.d.ts +5 -0
- package/dist/admin/shared/password-schema.js +10 -0
- package/dist/admin/styles/admin.css +1530 -151
- package/dist/admin/utils/formatDate.d.ts +1 -0
- package/dist/admin/utils/formatDate.js +8 -0
- package/dist/admin/utils/roleLabel.d.ts +2 -0
- package/dist/admin/utils/roleLabel.js +13 -0
- package/dist/ai-claude/index.d.ts +2 -0
- package/dist/ai-claude/index.js +56 -0
- package/dist/cms/runtime/api.d.ts +6 -1
- package/dist/cms/runtime/api.js +3 -0
- package/dist/cms/runtime/schemas.d.ts +9 -1
- package/dist/cms/runtime/schemas.js +8 -0
- package/dist/cms/runtime/types.d.ts +82 -10
- package/dist/cms/runtime/types.js +4 -0
- package/dist/components/ui/accordion/accordion.stories.svelte +39 -0
- package/dist/components/ui/accordion/accordion.stories.svelte.d.ts +27 -0
- package/dist/components/ui/alert/alert.stories.svelte +53 -0
- package/dist/components/ui/alert/alert.stories.svelte.d.ts +27 -0
- package/dist/components/ui/alert/alert.svelte +5 -0
- package/dist/components/ui/alert/alert.svelte.d.ts +9 -0
- package/dist/components/ui/avatar/avatar.stories.svelte +16 -0
- package/dist/components/ui/avatar/avatar.stories.svelte.d.ts +27 -0
- package/dist/components/ui/badge/badge.stories.svelte +33 -0
- package/dist/components/ui/badge/badge.stories.svelte.d.ts +27 -0
- package/dist/components/ui/breadcrumb/breadcrumb.stories.svelte +33 -0
- package/dist/components/ui/breadcrumb/breadcrumb.stories.svelte.d.ts +27 -0
- package/dist/components/ui/button/button.stories.svelte +43 -0
- package/dist/components/ui/button/button.stories.svelte.d.ts +27 -0
- package/dist/components/ui/button/button.svelte +1 -2
- package/dist/components/ui/button/button.svelte.d.ts +0 -3
- package/dist/components/ui/button-group/button-group-separator.svelte.d.ts +1 -1
- package/dist/components/ui/card/card.stories.svelte +42 -0
- package/dist/components/ui/card/card.stories.svelte.d.ts +27 -0
- package/dist/components/ui/command/command.stories.svelte +51 -0
- package/dist/components/ui/command/command.stories.svelte.d.ts +27 -0
- package/dist/components/ui/dialog/dialog.stories.svelte +29 -0
- package/dist/components/ui/dialog/dialog.stories.svelte.d.ts +27 -0
- package/dist/components/ui/field/field-label.svelte.d.ts +1 -1
- package/dist/components/ui/field/field.stories.svelte +21 -0
- package/dist/components/ui/field/field.stories.svelte.d.ts +27 -0
- package/dist/components/ui/input/input.stories.svelte +40 -0
- package/dist/components/ui/input/input.stories.svelte.d.ts +27 -0
- package/dist/components/ui/input/input.svelte +2 -4
- package/dist/components/ui/item/item-separator.svelte.d.ts +1 -1
- package/dist/components/ui/label/label.stories.svelte +20 -0
- package/dist/components/ui/label/label.stories.svelte.d.ts +27 -0
- package/dist/components/ui/popover/popover.stories.svelte +29 -0
- package/dist/components/ui/popover/popover.stories.svelte.d.ts +27 -0
- package/dist/components/ui/select/select-group-heading.svelte.d.ts +1 -1
- package/dist/components/ui/select/select.stories.svelte +23 -0
- package/dist/components/ui/select/select.stories.svelte.d.ts +27 -0
- package/dist/components/ui/separator/separator.stories.svelte +24 -0
- package/dist/components/ui/separator/separator.stories.svelte.d.ts +27 -0
- package/dist/components/ui/sheet/sheet.stories.svelte +29 -0
- package/dist/components/ui/sheet/sheet.stories.svelte.d.ts +27 -0
- package/dist/components/ui/sidebar/sidebar-group.svelte +3 -3
- package/dist/components/ui/sidebar/sidebar-group.svelte.d.ts +2 -2
- package/dist/components/ui/sidebar/sidebar-menu-button.svelte +28 -30
- package/dist/components/ui/sidebar/sidebar-menu-button.svelte.d.ts +7 -7
- package/dist/components/ui/sidebar/sidebar-separator.svelte.d.ts +1 -1
- package/dist/components/ui/sidebar/sidebar-trigger.svelte +4 -4
- package/dist/components/ui/sonner/sonner.stories.svelte +22 -0
- package/dist/components/ui/sonner/sonner.stories.svelte.d.ts +26 -0
- package/dist/components/ui/sonner/sonner.svelte +8 -2
- package/dist/components/ui/sonner/toast-demo.svelte +29 -0
- package/dist/components/ui/sonner/toast-demo.svelte.d.ts +6 -0
- package/dist/components/ui/textarea/textarea.stories.svelte +22 -0
- package/dist/components/ui/textarea/textarea.stories.svelte.d.ts +27 -0
- package/dist/components/ui/textarea/textarea.svelte +0 -2
- package/dist/components/ui/toggle/toggle.stories.svelte +22 -0
- package/dist/components/ui/toggle/toggle.stories.svelte.d.ts +27 -0
- package/dist/components/ui/toggle-group/toggle-group.stories.svelte +17 -0
- package/dist/components/ui/toggle-group/toggle-group.stories.svelte.d.ts +27 -0
- package/dist/components/ui/tooltip/tooltip.stories.svelte +26 -0
- package/dist/components/ui/tooltip/tooltip.stories.svelte.d.ts +27 -0
- package/dist/core/fields/fieldSchemaToTs.d.ts +1 -0
- package/dist/core/fields/fieldSchemaToTs.js +133 -1
- package/dist/core/fields/layoutUtils.d.ts +17 -0
- package/dist/core/fields/layoutUtils.js +149 -0
- package/dist/core/fields/structuredToHtml.d.ts +9 -0
- package/dist/core/fields/structuredToHtml.js +161 -0
- package/dist/core/server/entries/operations/create.js +2 -1
- package/dist/core/server/entries/operations/get.js +8 -6
- package/dist/core/server/entries/operations/update.d.ts +3 -0
- package/dist/core/server/entries/operations/update.js +30 -2
- package/dist/core/server/fields/queryStructuredContent.d.ts +15 -0
- package/dist/core/server/fields/queryStructuredContent.js +65 -0
- package/dist/core/server/fields/resolveImageFields.js +51 -2
- package/dist/core/server/fields/resolveRelationFields.js +2 -2
- package/dist/core/server/fields/resolveRichtextLinks.js +80 -13
- package/dist/core/server/fields/resolveUrlFields.js +57 -6
- package/dist/core/server/fields/slugResolver.d.ts +10 -0
- package/dist/core/server/fields/slugResolver.js +34 -0
- package/dist/core/server/generator/fields.js +15 -4
- package/dist/core/server/generator/generator.js +3 -2
- package/dist/files-local/index.js +126 -64
- package/dist/server/auth.d.ts +5 -0
- package/dist/server/auth.js +12 -1
- package/dist/sveltekit/components/structured-content.svelte +204 -0
- package/dist/sveltekit/components/structured-content.svelte.d.ts +21 -0
- package/dist/sveltekit/config.d.ts +13 -3
- package/dist/sveltekit/index.d.ts +3 -0
- package/dist/sveltekit/index.js +3 -0
- package/dist/sveltekit/server/handle.js +1 -0
- package/dist/types/config.d.ts +3 -0
- package/dist/types/fields.d.ts +19 -2
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/layout.d.ts +54 -0
- package/dist/types/layout.js +6 -0
- package/dist/types/structured-content.d.ts +63 -0
- package/dist/types/structured-content.js +1 -0
- package/dist/updates/0.1.5/index.d.ts +2 -0
- package/dist/updates/0.1.5/index.js +18 -0
- package/dist/updates/0.2.0/index.d.ts +2 -0
- package/dist/updates/0.2.0/index.js +11 -0
- package/dist/updates/0.2.2/index.d.ts +2 -0
- package/dist/updates/0.2.2/index.js +13 -0
- package/dist/updates/0.5.0/index.d.ts +2 -0
- package/dist/updates/0.5.0/index.js +14 -0
- package/dist/updates/index.js +5 -1
- package/package.json +16 -9
|
@@ -4,23 +4,27 @@
|
|
|
4
4
|
|
|
5
5
|
<script lang="ts" generics="T extends Record<string, unknown>">
|
|
6
6
|
import {
|
|
7
|
-
formFieldProxy,
|
|
8
|
-
type FormFieldProxy,
|
|
9
7
|
type FormPathLeaves,
|
|
10
8
|
type SuperForm
|
|
11
9
|
} from 'sveltekit-superforms';
|
|
12
10
|
import FieldRenderer from './field-renderer.svelte';
|
|
13
11
|
import { joinPath } from '../../utils/objectPath.js';
|
|
12
|
+
import { getAtPath, setAtPath } from '../../utils/objectPath.js';
|
|
14
13
|
import type {
|
|
15
14
|
Field,
|
|
16
15
|
ImageField,
|
|
17
|
-
ObjectFieldData,
|
|
18
16
|
SeoField,
|
|
19
17
|
SeoFieldData,
|
|
20
18
|
TextField
|
|
21
19
|
} from '../../../types/fields.js';
|
|
22
|
-
|
|
23
|
-
import
|
|
20
|
+
import { untrack } from 'svelte';
|
|
21
|
+
import slugify from '../../imports/slugify.js';
|
|
22
|
+
import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
|
|
23
|
+
import { getContentLanguage } from '../../state/content-language.svelte.js';
|
|
24
|
+
import { getLocalizedLabel } from '../../utils/collectionLabel.js';
|
|
25
|
+
|
|
26
|
+
const interfaceLanguage = useInterfaceLanguage();
|
|
27
|
+
const contentLanguage = getContentLanguage();
|
|
24
28
|
|
|
25
29
|
type Props = {
|
|
26
30
|
field: SeoField;
|
|
@@ -30,31 +34,89 @@
|
|
|
30
34
|
|
|
31
35
|
let { field, form, path, ...props }: Props = $props();
|
|
32
36
|
|
|
37
|
+
const { form: formData } = form;
|
|
38
|
+
|
|
39
|
+
const labels = {
|
|
40
|
+
slug: {
|
|
41
|
+
label: { en: 'URL Slug', pl: 'Slug URL' },
|
|
42
|
+
description: {
|
|
43
|
+
en: 'Page address shown in the browser bar',
|
|
44
|
+
pl: 'Adres strony widoczny w pasku przeglądarki'
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
canonicalUrl: {
|
|
48
|
+
label: { en: 'Canonical URL', pl: 'Adres kanoniczny' },
|
|
49
|
+
description: {
|
|
50
|
+
en: 'If this content appears at another address, enter the original URL',
|
|
51
|
+
pl: 'Jeśli ta treść pojawia się pod innym adresem, podaj oryginalny URL'
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
title: {
|
|
55
|
+
label: { en: 'SEO Title', pl: 'Tytuł SEO' },
|
|
56
|
+
description: {
|
|
57
|
+
en: 'Appears in search results and browser tabs (50–60 characters)',
|
|
58
|
+
pl: 'Widoczny w wynikach wyszukiwania i zakładce przeglądarki (50–60 znaków)'
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
description: {
|
|
62
|
+
label: { en: 'Description', pl: 'Opis' },
|
|
63
|
+
description: {
|
|
64
|
+
en: 'Short summary for search engines (120–160 characters)',
|
|
65
|
+
pl: 'Krótkie podsumowanie dla wyszukiwarek (120–160 znaków)'
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
keywords: {
|
|
69
|
+
label: { en: 'Keywords', pl: 'Słowa kluczowe' },
|
|
70
|
+
description: {
|
|
71
|
+
en: 'Comma-separated list of keywords',
|
|
72
|
+
pl: 'Lista słów kluczowych oddzielonych przecinkami'
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
ogImage: {
|
|
76
|
+
label: { en: 'Social media image', pl: 'Obraz w mediach społecznościowych' },
|
|
77
|
+
description: {
|
|
78
|
+
en: 'Image displayed when sharing this page on social media',
|
|
79
|
+
pl: 'Obraz wyświetlany przy udostępnianiu strony w mediach społecznościowych'
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
customCode: {
|
|
83
|
+
label: { en: 'Custom code', pl: 'Własny kod' },
|
|
84
|
+
description: {
|
|
85
|
+
en: 'Additional HTML inserted into the page <head>',
|
|
86
|
+
pl: 'Dodatkowy HTML wstawiany do sekcji <head> strony'
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
33
91
|
const slugField: TextField = {
|
|
34
92
|
type: 'text',
|
|
35
93
|
slug: 'slug',
|
|
36
|
-
label:
|
|
94
|
+
label: labels.slug.label,
|
|
95
|
+
description: labels.slug.description,
|
|
37
96
|
required: true
|
|
38
97
|
};
|
|
39
98
|
|
|
40
99
|
const canonicalUrlField: TextField = {
|
|
41
100
|
type: 'text',
|
|
42
101
|
slug: 'canonicalUrl',
|
|
43
|
-
label:
|
|
102
|
+
label: labels.canonicalUrl.label,
|
|
103
|
+
description: labels.canonicalUrl.description,
|
|
44
104
|
required: false
|
|
45
105
|
};
|
|
46
106
|
|
|
47
107
|
const titleField: TextField = {
|
|
48
108
|
type: 'text',
|
|
49
109
|
slug: 'title',
|
|
50
|
-
label:
|
|
110
|
+
label: labels.title.label,
|
|
111
|
+
description: labels.title.description,
|
|
51
112
|
required: true
|
|
52
113
|
};
|
|
53
114
|
|
|
54
115
|
const descriptionField: TextField = {
|
|
55
116
|
type: 'text',
|
|
56
117
|
slug: 'description',
|
|
57
|
-
label:
|
|
118
|
+
label: labels.description.label,
|
|
119
|
+
description: labels.description.description,
|
|
58
120
|
required: false,
|
|
59
121
|
multiline: true
|
|
60
122
|
};
|
|
@@ -62,7 +124,8 @@
|
|
|
62
124
|
const keyWordsField: TextField = {
|
|
63
125
|
type: 'text',
|
|
64
126
|
slug: 'keywords',
|
|
65
|
-
label:
|
|
127
|
+
label: labels.keywords.label,
|
|
128
|
+
description: labels.keywords.description,
|
|
66
129
|
required: false,
|
|
67
130
|
multiline: true
|
|
68
131
|
};
|
|
@@ -70,14 +133,16 @@
|
|
|
70
133
|
const ogImageField: ImageField = {
|
|
71
134
|
type: 'image',
|
|
72
135
|
slug: 'ogImage',
|
|
73
|
-
label:
|
|
136
|
+
label: labels.ogImage.label,
|
|
137
|
+
description: labels.ogImage.description,
|
|
74
138
|
required: false
|
|
75
139
|
};
|
|
76
140
|
|
|
77
141
|
const customCodeField: TextField = {
|
|
78
142
|
type: 'text',
|
|
79
143
|
slug: 'customCode',
|
|
80
|
-
label:
|
|
144
|
+
label: labels.customCode.label,
|
|
145
|
+
description: labels.customCode.description,
|
|
81
146
|
required: false,
|
|
82
147
|
multiline: true
|
|
83
148
|
};
|
|
@@ -91,19 +156,134 @@
|
|
|
91
156
|
ogImageField,
|
|
92
157
|
customCodeField
|
|
93
158
|
];
|
|
159
|
+
|
|
160
|
+
// Helper: read a value from formData, handling both localized (object) and plain (string) values
|
|
161
|
+
function readSourceValue(sourcePath: string): string | undefined {
|
|
162
|
+
const raw = ($formData as Record<string, unknown>)[sourcePath];
|
|
163
|
+
if (typeof raw === 'string') return raw;
|
|
164
|
+
if (raw && typeof raw === 'object') {
|
|
165
|
+
return (raw as Record<string, string>)[contentLanguage.current];
|
|
166
|
+
}
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Helper: read value at a dot-path from formData
|
|
171
|
+
function readPath(dotPath: string): string | undefined {
|
|
172
|
+
const val = getAtPath($formData as Record<string, unknown>, dotPath);
|
|
173
|
+
return typeof val === 'string' ? val : undefined;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Character count for current language
|
|
177
|
+
let titleLength = $derived.by(() => {
|
|
178
|
+
const val = readPath(joinPath(String(path), 'title', contentLanguage.current));
|
|
179
|
+
return val?.length ?? 0;
|
|
180
|
+
});
|
|
181
|
+
let descLength = $derived.by(() => {
|
|
182
|
+
const val = readPath(joinPath(String(path), 'description', contentLanguage.current));
|
|
183
|
+
return val?.length ?? 0;
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
function charHintClass(len: number, min: number, max: number): string {
|
|
187
|
+
if (len === 0) return 'text-muted-foreground';
|
|
188
|
+
if (len >= min && len <= max) return 'text-success';
|
|
189
|
+
if (len < min) return 'text-warning';
|
|
190
|
+
return 'text-destructive';
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Auto-gen: track last auto-generated values per language
|
|
194
|
+
let lastAutoSlugs: Record<string, string> = {};
|
|
195
|
+
let lastAutoTitles: Record<string, string> = {};
|
|
196
|
+
|
|
197
|
+
// slugSource → auto-gen seo.slug per language
|
|
198
|
+
$effect(() => {
|
|
199
|
+
if (!field.slugSource) return;
|
|
200
|
+
const sourceRaw = ($formData as Record<string, unknown>)[field.slugSource];
|
|
201
|
+
if (!sourceRaw) return;
|
|
202
|
+
|
|
203
|
+
untrack(() => {
|
|
204
|
+
const pairs: [string, string][] =
|
|
205
|
+
typeof sourceRaw === 'string'
|
|
206
|
+
? [[contentLanguage.current, sourceRaw]]
|
|
207
|
+
: typeof sourceRaw === 'object'
|
|
208
|
+
? Object.entries(sourceRaw as Record<string, string>).filter(([, v]) => typeof v === 'string' && v)
|
|
209
|
+
: [];
|
|
210
|
+
|
|
211
|
+
let changed = false;
|
|
212
|
+
for (const [lang, text] of pairs) {
|
|
213
|
+
const targetPath = joinPath(String(path), 'slug', lang);
|
|
214
|
+
const current = getAtPath($formData as Record<string, unknown>, targetPath) as string | undefined;
|
|
215
|
+
if (!current || current === lastAutoSlugs[lang]) {
|
|
216
|
+
const newSlug = slugify(String(text), { lower: true, strict: true, trim: true });
|
|
217
|
+
if (newSlug !== current) {
|
|
218
|
+
setAtPath($formData as Record<string, unknown>, targetPath, newSlug);
|
|
219
|
+
changed = true;
|
|
220
|
+
}
|
|
221
|
+
lastAutoSlugs[lang] = newSlug;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (changed) $formData = $formData;
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// titleSource → auto-fill seo.title per language
|
|
229
|
+
$effect(() => {
|
|
230
|
+
if (!field.titleSource) return;
|
|
231
|
+
const sourceRaw = ($formData as Record<string, unknown>)[field.titleSource];
|
|
232
|
+
if (!sourceRaw) return;
|
|
233
|
+
|
|
234
|
+
untrack(() => {
|
|
235
|
+
const pairs: [string, string][] =
|
|
236
|
+
typeof sourceRaw === 'string'
|
|
237
|
+
? [[contentLanguage.current, sourceRaw]]
|
|
238
|
+
: typeof sourceRaw === 'object'
|
|
239
|
+
? Object.entries(sourceRaw as Record<string, string>).filter(([, v]) => typeof v === 'string' && v)
|
|
240
|
+
: [];
|
|
241
|
+
|
|
242
|
+
let changed = false;
|
|
243
|
+
for (const [lang, text] of pairs) {
|
|
244
|
+
const targetPath = joinPath(String(path), 'title', lang);
|
|
245
|
+
const current = getAtPath($formData as Record<string, unknown>, targetPath) as string | undefined;
|
|
246
|
+
if (!current || current === lastAutoTitles[lang]) {
|
|
247
|
+
if (text !== current) {
|
|
248
|
+
setAtPath($formData as Record<string, unknown>, targetPath, text);
|
|
249
|
+
changed = true;
|
|
250
|
+
}
|
|
251
|
+
lastAutoTitles[lang] = text;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (changed) $formData = $formData;
|
|
255
|
+
});
|
|
256
|
+
});
|
|
94
257
|
</script>
|
|
95
258
|
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
<
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
259
|
+
<div class="space-y-4">
|
|
260
|
+
{#each fields as f}
|
|
261
|
+
<div>
|
|
262
|
+
<FieldRenderer
|
|
263
|
+
field={f}
|
|
264
|
+
form={form as SuperForm<Record<string, unknown>>}
|
|
265
|
+
path={joinPath(path, f.slug)}
|
|
266
|
+
/>
|
|
267
|
+
{#if f.slug === 'title'}
|
|
268
|
+
<p class="mt-1 text-xs {charHintClass(titleLength, 50, 60)}">
|
|
269
|
+
{titleLength}/60
|
|
270
|
+
{#if titleLength > 0 && titleLength < 50}
|
|
271
|
+
— {getLocalizedLabel({ en: 'a bit short', pl: 'trochę za krótko' }, interfaceLanguage.current)}
|
|
272
|
+
{:else if titleLength > 60}
|
|
273
|
+
— {getLocalizedLabel({ en: 'too long', pl: 'za długo' }, interfaceLanguage.current)}
|
|
274
|
+
{/if}
|
|
275
|
+
</p>
|
|
276
|
+
{/if}
|
|
277
|
+
{#if f.slug === 'description'}
|
|
278
|
+
<p class="mt-1 text-xs {charHintClass(descLength, 120, 160)}">
|
|
279
|
+
{descLength}/160
|
|
280
|
+
{#if descLength > 0 && descLength < 120}
|
|
281
|
+
— {getLocalizedLabel({ en: 'a bit short', pl: 'trochę za krótko' }, interfaceLanguage.current)}
|
|
282
|
+
{:else if descLength > 160}
|
|
283
|
+
— {getLocalizedLabel({ en: 'too long', pl: 'za długo' }, interfaceLanguage.current)}
|
|
284
|
+
{/if}
|
|
285
|
+
</p>
|
|
286
|
+
{/if}
|
|
107
287
|
</div>
|
|
108
|
-
|
|
109
|
-
</
|
|
288
|
+
{/each}
|
|
289
|
+
</div>
|
|
@@ -0,0 +1,289 @@
|
|
|
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 Button from '../../../components/ui/button/button.svelte';
|
|
7
|
+
import Input from '../../../components/ui/input/input.svelte';
|
|
8
|
+
import {
|
|
9
|
+
formFieldProxy,
|
|
10
|
+
type FormFieldProxy,
|
|
11
|
+
type FormPathLeaves,
|
|
12
|
+
type SuperForm
|
|
13
|
+
} from 'sveltekit-superforms';
|
|
14
|
+
import type { ArrayField, UrlFieldData } from '../../../types/fields.js';
|
|
15
|
+
import { onMount } from 'svelte';
|
|
16
|
+
import CirclePlus from '@tabler/icons-svelte/icons/circle-plus';
|
|
17
|
+
import X from '@tabler/icons-svelte/icons/x';
|
|
18
|
+
import { getContentLanguage } from '../../state/content-language.svelte.js';
|
|
19
|
+
import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
|
|
20
|
+
|
|
21
|
+
const contentLanguage = getContentLanguage();
|
|
22
|
+
const interfaceLanguage = useInterfaceLanguage();
|
|
23
|
+
|
|
24
|
+
type Props = {
|
|
25
|
+
field: ArrayField;
|
|
26
|
+
form: SuperForm<T>;
|
|
27
|
+
path: FormPathLeaves<T>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
let { field, form, path, ...props }: Props = $props();
|
|
31
|
+
|
|
32
|
+
const { value } = formFieldProxy(form, path) satisfies FormFieldProxy<unknown[] | undefined>;
|
|
33
|
+
|
|
34
|
+
onMount(() => {
|
|
35
|
+
if (!$value || !Array.isArray($value)) {
|
|
36
|
+
$value = [];
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const atMax = $derived(field.maxItems !== undefined && ($value?.length ?? 0) >= field.maxItems);
|
|
41
|
+
|
|
42
|
+
// --- Text ---
|
|
43
|
+
let textInput = $state('');
|
|
44
|
+
|
|
45
|
+
function addTextItem() {
|
|
46
|
+
if (!textInput.trim() || atMax) return;
|
|
47
|
+
if (field.localized) {
|
|
48
|
+
const item: Record<string, string> = {};
|
|
49
|
+
item[contentLanguage.current] = textInput.trim();
|
|
50
|
+
$value = [...($value ?? []), item];
|
|
51
|
+
} else {
|
|
52
|
+
$value = [...($value ?? []), textInput.trim()];
|
|
53
|
+
}
|
|
54
|
+
textInput = '';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function handleTextKeydown(e: KeyboardEvent) {
|
|
58
|
+
if (e.key === 'Enter') {
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
addTextItem();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// --- Number ---
|
|
65
|
+
let numberInput = $state('');
|
|
66
|
+
|
|
67
|
+
function addNumberItem() {
|
|
68
|
+
const num = Number(numberInput);
|
|
69
|
+
if (numberInput === '' || isNaN(num) || atMax) return;
|
|
70
|
+
$value = [...($value ?? []), num];
|
|
71
|
+
numberInput = '';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function handleNumberKeydown(e: KeyboardEvent) {
|
|
75
|
+
if (e.key === 'Enter') {
|
|
76
|
+
e.preventDefault();
|
|
77
|
+
addNumberItem();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// --- URL ---
|
|
82
|
+
function addUrlItem() {
|
|
83
|
+
if (atMax) return;
|
|
84
|
+
const item: UrlFieldData = { url: {}, text: {}, newTab: false };
|
|
85
|
+
$value = [...($value ?? []), item];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// --- Common ---
|
|
89
|
+
function removeItem(index: number) {
|
|
90
|
+
if (!$value) return;
|
|
91
|
+
$value = $value.filter((_, i) => i !== index);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function getTextDisplay(item: unknown): string {
|
|
95
|
+
if (typeof item === 'string') return item;
|
|
96
|
+
if (typeof item === 'object' && item !== null) {
|
|
97
|
+
const rec = item as Record<string, string>;
|
|
98
|
+
return rec[contentLanguage.current] || Object.values(rec)[0] || '';
|
|
99
|
+
}
|
|
100
|
+
return String(item);
|
|
101
|
+
}
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
{#if field.of === 'text'}
|
|
105
|
+
<!-- Tag chips for text items -->
|
|
106
|
+
<div class="space-y-3">
|
|
107
|
+
{#if $value && $value.length > 0}
|
|
108
|
+
<div class="flex flex-wrap gap-2">
|
|
109
|
+
{#each $value as item, index}
|
|
110
|
+
<span
|
|
111
|
+
class="bg-[#EEEAF8] text-[#1A1A2E] inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium"
|
|
112
|
+
>
|
|
113
|
+
{#if field.localized}
|
|
114
|
+
<!-- Localized: show editable input per current lang -->
|
|
115
|
+
<input
|
|
116
|
+
type="text"
|
|
117
|
+
class="bg-transparent border-none outline-none w-auto min-w-[4ch] text-sm"
|
|
118
|
+
style="width: {getTextDisplay(item).length + 1}ch"
|
|
119
|
+
value={getTextDisplay(item)}
|
|
120
|
+
oninput={(e) => {
|
|
121
|
+
const val = e.currentTarget.value;
|
|
122
|
+
if (typeof item === 'object' && item !== null) {
|
|
123
|
+
const rec = { ...(item as Record<string, string>) };
|
|
124
|
+
rec[contentLanguage.current] = val;
|
|
125
|
+
const arr = [...($value ?? [])];
|
|
126
|
+
arr[index] = rec;
|
|
127
|
+
$value = arr;
|
|
128
|
+
}
|
|
129
|
+
}}
|
|
130
|
+
/>
|
|
131
|
+
{:else}
|
|
132
|
+
{item}
|
|
133
|
+
{/if}
|
|
134
|
+
<button
|
|
135
|
+
type="button"
|
|
136
|
+
class="text-[#555566] hover:text-[#C44B4B] transition-colors"
|
|
137
|
+
onclick={() => removeItem(index)}
|
|
138
|
+
aria-label="Usuń element"
|
|
139
|
+
>
|
|
140
|
+
<X class="h-3.5 w-3.5" />
|
|
141
|
+
</button>
|
|
142
|
+
</span>
|
|
143
|
+
{/each}
|
|
144
|
+
</div>
|
|
145
|
+
{/if}
|
|
146
|
+
|
|
147
|
+
<div class="flex items-center gap-2">
|
|
148
|
+
<Input
|
|
149
|
+
type="text"
|
|
150
|
+
placeholder="Wpisz i naciśnij Enter..."
|
|
151
|
+
bind:value={textInput}
|
|
152
|
+
onkeydown={handleTextKeydown}
|
|
153
|
+
disabled={atMax}
|
|
154
|
+
class="max-w-xs"
|
|
155
|
+
/>
|
|
156
|
+
<Button size="sm" type="button" variant="outline" disabled={atMax || !textInput.trim()} onclick={addTextItem}>
|
|
157
|
+
<CirclePlus class="h-4 w-4" />
|
|
158
|
+
Dodaj
|
|
159
|
+
</Button>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
{#if field.maxItems !== undefined}
|
|
163
|
+
<p class="text-xs {atMax ? 'text-destructive' : 'text-muted-foreground'}">
|
|
164
|
+
{$value?.length ?? 0} / {field.maxItems}
|
|
165
|
+
</p>
|
|
166
|
+
{/if}
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
{:else if field.of === 'number'}
|
|
170
|
+
<!-- Tag chips for number items -->
|
|
171
|
+
<div class="space-y-3">
|
|
172
|
+
{#if $value && $value.length > 0}
|
|
173
|
+
<div class="flex flex-wrap gap-2">
|
|
174
|
+
{#each $value as item, index}
|
|
175
|
+
<span
|
|
176
|
+
class="bg-[#EEEAF8] text-[#1A1A2E] inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium tabular-nums"
|
|
177
|
+
>
|
|
178
|
+
{item}
|
|
179
|
+
<button
|
|
180
|
+
type="button"
|
|
181
|
+
class="text-[#555566] hover:text-[#C44B4B] transition-colors"
|
|
182
|
+
onclick={() => removeItem(index)}
|
|
183
|
+
aria-label="Usuń element"
|
|
184
|
+
>
|
|
185
|
+
<X class="h-3.5 w-3.5" />
|
|
186
|
+
</button>
|
|
187
|
+
</span>
|
|
188
|
+
{/each}
|
|
189
|
+
</div>
|
|
190
|
+
{/if}
|
|
191
|
+
|
|
192
|
+
<div class="flex items-center gap-2">
|
|
193
|
+
<Input
|
|
194
|
+
type="number"
|
|
195
|
+
placeholder="Wpisz liczbę..."
|
|
196
|
+
bind:value={numberInput}
|
|
197
|
+
onkeydown={handleNumberKeydown}
|
|
198
|
+
disabled={atMax}
|
|
199
|
+
class="max-w-[160px]"
|
|
200
|
+
/>
|
|
201
|
+
<Button size="sm" type="button" variant="outline" disabled={atMax || numberInput === ''} onclick={addNumberItem}>
|
|
202
|
+
<CirclePlus class="h-4 w-4" />
|
|
203
|
+
Dodaj
|
|
204
|
+
</Button>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
{#if field.maxItems !== undefined}
|
|
208
|
+
<p class="text-xs {atMax ? 'text-destructive' : 'text-muted-foreground'}">
|
|
209
|
+
{$value?.length ?? 0} / {field.maxItems}
|
|
210
|
+
</p>
|
|
211
|
+
{/if}
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
{:else if field.of === 'url'}
|
|
215
|
+
<!-- Compact list for URL items -->
|
|
216
|
+
<div class="space-y-3">
|
|
217
|
+
{#if $value && $value.length > 0}
|
|
218
|
+
<div class="space-y-2">
|
|
219
|
+
{#each $value as item, index}
|
|
220
|
+
{@const urlItem = item as UrlFieldData}
|
|
221
|
+
<div class="flex items-start gap-2 rounded-xl border border-[#E2DFF0] bg-[#F4F2FA] p-3">
|
|
222
|
+
<div class="flex-1 space-y-2">
|
|
223
|
+
<Input
|
|
224
|
+
type="url"
|
|
225
|
+
placeholder="URL..."
|
|
226
|
+
value={urlItem.url?.[contentLanguage.current] ?? ''}
|
|
227
|
+
oninput={(e) => {
|
|
228
|
+
const val = e.currentTarget.value;
|
|
229
|
+
const updated = { ...urlItem, url: { ...(urlItem.url ?? {}), [contentLanguage.current]: val } };
|
|
230
|
+
const arr = [...($value ?? [])];
|
|
231
|
+
arr[index] = updated;
|
|
232
|
+
$value = arr;
|
|
233
|
+
}}
|
|
234
|
+
/>
|
|
235
|
+
<div class="flex items-center gap-2">
|
|
236
|
+
<Input
|
|
237
|
+
type="text"
|
|
238
|
+
placeholder="Tekst linku..."
|
|
239
|
+
class="flex-1"
|
|
240
|
+
value={urlItem.text?.[contentLanguage.current] ?? ''}
|
|
241
|
+
oninput={(e) => {
|
|
242
|
+
const val = e.currentTarget.value;
|
|
243
|
+
const updated = { ...urlItem, text: { ...(urlItem.text ?? {}), [contentLanguage.current]: val } };
|
|
244
|
+
const arr = [...($value ?? [])];
|
|
245
|
+
arr[index] = updated;
|
|
246
|
+
$value = arr;
|
|
247
|
+
}}
|
|
248
|
+
/>
|
|
249
|
+
<label class="flex items-center gap-1.5 text-xs text-[#555566] whitespace-nowrap">
|
|
250
|
+
<input
|
|
251
|
+
type="checkbox"
|
|
252
|
+
checked={urlItem.newTab ?? false}
|
|
253
|
+
onchange={(e) => {
|
|
254
|
+
const updated = { ...urlItem, newTab: e.currentTarget.checked };
|
|
255
|
+
const arr = [...($value ?? [])];
|
|
256
|
+
arr[index] = updated;
|
|
257
|
+
$value = arr;
|
|
258
|
+
}}
|
|
259
|
+
class="accent-[#5B4A9E]"
|
|
260
|
+
/>
|
|
261
|
+
Nowa karta
|
|
262
|
+
</label>
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
<button
|
|
266
|
+
type="button"
|
|
267
|
+
class="mt-1.5 text-[#555566] hover:text-[#C44B4B] transition-colors"
|
|
268
|
+
onclick={() => removeItem(index)}
|
|
269
|
+
aria-label="Usuń link"
|
|
270
|
+
>
|
|
271
|
+
<X class="h-4 w-4" />
|
|
272
|
+
</button>
|
|
273
|
+
</div>
|
|
274
|
+
{/each}
|
|
275
|
+
</div>
|
|
276
|
+
{/if}
|
|
277
|
+
|
|
278
|
+
<Button size="sm" type="button" variant="outline" disabled={atMax} onclick={addUrlItem}>
|
|
279
|
+
<CirclePlus class="h-4 w-4" />
|
|
280
|
+
Dodaj link
|
|
281
|
+
</Button>
|
|
282
|
+
|
|
283
|
+
{#if field.maxItems !== undefined}
|
|
284
|
+
<p class="text-xs {atMax ? 'text-destructive' : 'text-muted-foreground'}">
|
|
285
|
+
{$value?.length ?? 0} / {field.maxItems}
|
|
286
|
+
</p>
|
|
287
|
+
{/if}
|
|
288
|
+
</div>
|
|
289
|
+
{/if}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type FormPathLeaves, type SuperForm } from 'sveltekit-superforms';
|
|
2
|
+
import type { ArrayField } from '../../../types/fields.js';
|
|
3
|
+
declare function $$render<T extends Record<string, unknown>>(): {
|
|
4
|
+
props: {
|
|
5
|
+
field: ArrayField;
|
|
6
|
+
form: SuperForm<T>;
|
|
7
|
+
path: FormPathLeaves<T>;
|
|
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 SimpleArrayField: $$IsomorphicComponent;
|
|
29
|
+
type SimpleArrayField<T extends Record<string, unknown>> = InstanceType<typeof SimpleArrayField<T>>;
|
|
30
|
+
export default SimpleArrayField;
|
|
@@ -22,13 +22,14 @@
|
|
|
22
22
|
|
|
23
23
|
let { field, form, path, ...props }: Props = $props();
|
|
24
24
|
|
|
25
|
-
let
|
|
25
|
+
let effectivePattern = field.pattern || (field.sourceField ? `{${field.sourceField}}` : undefined);
|
|
26
|
+
let pattern = effectivePattern;
|
|
26
27
|
|
|
27
28
|
const { form: formData } = form;
|
|
28
29
|
|
|
29
30
|
const { value } = formFieldProxy(form, path) satisfies FormFieldProxy<string | undefined>;
|
|
30
31
|
|
|
31
|
-
let patternKeys =
|
|
32
|
+
let patternKeys = effectivePattern ? extractPatternKeys(effectivePattern) : [];
|
|
32
33
|
|
|
33
34
|
function extractPatternKeys(pattern: string): string[] {
|
|
34
35
|
const matches = [...pattern.matchAll(/{([^}]+)}/g)];
|