@karibsen/eponyme 0.1.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/LICENSE +21 -0
- package/README.md +609 -0
- package/dist/eponyme.d.mts +15 -0
- package/dist/eponyme.mjs +38 -0
- package/dist/module.d.mts +26 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +154 -0
- package/dist/runtime/assets/dashboard.css +1 -0
- package/dist/runtime/components/editor/EponymeCollectionPage.d.vue.ts +9 -0
- package/dist/runtime/components/editor/EponymeCollectionPage.vue +238 -0
- package/dist/runtime/components/editor/EponymeCollectionPage.vue.d.ts +9 -0
- package/dist/runtime/components/editor/EponymeCopy.d.vue.ts +7 -0
- package/dist/runtime/components/editor/EponymeCopy.vue +28 -0
- package/dist/runtime/components/editor/EponymeCopy.vue.d.ts +7 -0
- package/dist/runtime/components/editor/EponymeFolderNav.d.vue.ts +7 -0
- package/dist/runtime/components/editor/EponymeFolderNav.vue +146 -0
- package/dist/runtime/components/editor/EponymeFolderNav.vue.d.ts +7 -0
- package/dist/runtime/components/editor/EponymeFolderPage.d.vue.ts +7 -0
- package/dist/runtime/components/editor/EponymeFolderPage.vue +62 -0
- package/dist/runtime/components/editor/EponymeFolderPage.vue.d.ts +7 -0
- package/dist/runtime/components/editor/EponymeForm.d.vue.ts +9 -0
- package/dist/runtime/components/editor/EponymeForm.vue +309 -0
- package/dist/runtime/components/editor/EponymeForm.vue.d.ts +9 -0
- package/dist/runtime/components/editor/EponymeFormPage.d.vue.ts +8 -0
- package/dist/runtime/components/editor/EponymeFormPage.vue +288 -0
- package/dist/runtime/components/editor/EponymeFormPage.vue.d.ts +8 -0
- package/dist/runtime/components/editor/EponymeHistoryDialog.d.vue.ts +14 -0
- package/dist/runtime/components/editor/EponymeHistoryDialog.vue +97 -0
- package/dist/runtime/components/editor/EponymeHistoryDialog.vue.d.ts +14 -0
- package/dist/runtime/components/editor/EponymeNavigationLink.d.vue.ts +14 -0
- package/dist/runtime/components/editor/EponymeNavigationLink.vue +80 -0
- package/dist/runtime/components/editor/EponymeNavigationLink.vue.d.ts +14 -0
- package/dist/runtime/components/editor/EponymePageNavigation.d.vue.ts +8 -0
- package/dist/runtime/components/editor/EponymePageNavigation.vue +58 -0
- package/dist/runtime/components/editor/EponymePageNavigation.vue.d.ts +8 -0
- package/dist/runtime/components/editor/EponymePreviewPanel.d.vue.ts +16 -0
- package/dist/runtime/components/editor/EponymePreviewPanel.vue +100 -0
- package/dist/runtime/components/editor/EponymePreviewPanel.vue.d.ts +16 -0
- package/dist/runtime/components/editor/EponymeSidebar.d.vue.ts +6 -0
- package/dist/runtime/components/editor/EponymeSidebar.vue +244 -0
- package/dist/runtime/components/editor/EponymeSidebar.vue.d.ts +6 -0
- package/dist/runtime/components/editor/EponymeSidebarTree.d.vue.ts +18 -0
- package/dist/runtime/components/editor/EponymeSidebarTree.vue +84 -0
- package/dist/runtime/components/editor/EponymeSidebarTree.vue.d.ts +18 -0
- package/dist/runtime/components/fields/ArrayField.d.vue.ts +19 -0
- package/dist/runtime/components/fields/ArrayField.vue +198 -0
- package/dist/runtime/components/fields/ArrayField.vue.d.ts +19 -0
- package/dist/runtime/components/fields/BooleanField.d.vue.ts +16 -0
- package/dist/runtime/components/fields/BooleanField.vue +36 -0
- package/dist/runtime/components/fields/BooleanField.vue.d.ts +16 -0
- package/dist/runtime/components/fields/CheckboxGroupField.d.vue.ts +20 -0
- package/dist/runtime/components/fields/CheckboxGroupField.vue +52 -0
- package/dist/runtime/components/fields/CheckboxGroupField.vue.d.ts +20 -0
- package/dist/runtime/components/fields/ColorField.d.vue.ts +19 -0
- package/dist/runtime/components/fields/ColorField.vue +39 -0
- package/dist/runtime/components/fields/ColorField.vue.d.ts +19 -0
- package/dist/runtime/components/fields/DateField.d.vue.ts +18 -0
- package/dist/runtime/components/fields/DateField.vue +39 -0
- package/dist/runtime/components/fields/DateField.vue.d.ts +18 -0
- package/dist/runtime/components/fields/FieldRenderer.d.vue.ts +24 -0
- package/dist/runtime/components/fields/FieldRenderer.vue +135 -0
- package/dist/runtime/components/fields/FieldRenderer.vue.d.ts +24 -0
- package/dist/runtime/components/fields/NumberField.d.vue.ts +20 -0
- package/dist/runtime/components/fields/NumberField.vue +58 -0
- package/dist/runtime/components/fields/NumberField.vue.d.ts +20 -0
- package/dist/runtime/components/fields/RadioField.d.vue.ts +20 -0
- package/dist/runtime/components/fields/RadioField.vue +44 -0
- package/dist/runtime/components/fields/RadioField.vue.d.ts +20 -0
- package/dist/runtime/components/fields/RichTextField.d.vue.ts +20 -0
- package/dist/runtime/components/fields/RichTextField.vue +176 -0
- package/dist/runtime/components/fields/RichTextField.vue.d.ts +20 -0
- package/dist/runtime/components/fields/SectionField.d.vue.ts +20 -0
- package/dist/runtime/components/fields/SectionField.vue +127 -0
- package/dist/runtime/components/fields/SectionField.vue.d.ts +20 -0
- package/dist/runtime/components/fields/SelectField.d.vue.ts +21 -0
- package/dist/runtime/components/fields/SelectField.vue +42 -0
- package/dist/runtime/components/fields/SelectField.vue.d.ts +21 -0
- package/dist/runtime/components/fields/SlugField.d.vue.ts +20 -0
- package/dist/runtime/components/fields/SlugField.vue +35 -0
- package/dist/runtime/components/fields/SlugField.vue.d.ts +20 -0
- package/dist/runtime/components/fields/TabsField.d.vue.ts +19 -0
- package/dist/runtime/components/fields/TabsField.vue +109 -0
- package/dist/runtime/components/fields/TabsField.vue.d.ts +19 -0
- package/dist/runtime/components/fields/TextField.d.vue.ts +22 -0
- package/dist/runtime/components/fields/TextField.vue +56 -0
- package/dist/runtime/components/fields/TextField.vue.d.ts +22 -0
- package/dist/runtime/components/fields/TextareaField.d.vue.ts +20 -0
- package/dist/runtime/components/fields/TextareaField.vue +47 -0
- package/dist/runtime/components/fields/TextareaField.vue.d.ts +20 -0
- package/dist/runtime/components/fields/UrlField.d.vue.ts +17 -0
- package/dist/runtime/components/fields/UrlField.vue +35 -0
- package/dist/runtime/components/fields/UrlField.vue.d.ts +17 -0
- package/dist/runtime/components/ui/EPAccordion.d.vue.ts +26 -0
- package/dist/runtime/components/ui/EPAccordion.vue +64 -0
- package/dist/runtime/components/ui/EPAccordion.vue.d.ts +26 -0
- package/dist/runtime/components/ui/EPAvatar.d.vue.ts +9 -0
- package/dist/runtime/components/ui/EPAvatar.vue +25 -0
- package/dist/runtime/components/ui/EPAvatar.vue.d.ts +9 -0
- package/dist/runtime/components/ui/EPBadge.d.vue.ts +16 -0
- package/dist/runtime/components/ui/EPBadge.vue +17 -0
- package/dist/runtime/components/ui/EPBadge.vue.d.ts +16 -0
- package/dist/runtime/components/ui/EPButton.d.vue.ts +22 -0
- package/dist/runtime/components/ui/EPButton.vue +55 -0
- package/dist/runtime/components/ui/EPButton.vue.d.ts +22 -0
- package/dist/runtime/components/ui/EPCheckbox.d.vue.ts +11 -0
- package/dist/runtime/components/ui/EPCheckbox.vue +21 -0
- package/dist/runtime/components/ui/EPCheckbox.vue.d.ts +11 -0
- package/dist/runtime/components/ui/EPColorPicker.d.vue.ts +19 -0
- package/dist/runtime/components/ui/EPColorPicker.vue +63 -0
- package/dist/runtime/components/ui/EPColorPicker.vue.d.ts +19 -0
- package/dist/runtime/components/ui/EPDialog.d.vue.ts +24 -0
- package/dist/runtime/components/ui/EPDialog.vue +54 -0
- package/dist/runtime/components/ui/EPDialog.vue.d.ts +24 -0
- package/dist/runtime/components/ui/EPDropdownMenu.d.vue.ts +25 -0
- package/dist/runtime/components/ui/EPDropdownMenu.vue +40 -0
- package/dist/runtime/components/ui/EPDropdownMenu.vue.d.ts +25 -0
- package/dist/runtime/components/ui/EPFormField.d.vue.ts +24 -0
- package/dist/runtime/components/ui/EPFormField.vue +62 -0
- package/dist/runtime/components/ui/EPFormField.vue.d.ts +24 -0
- package/dist/runtime/components/ui/EPInputText.d.vue.ts +17 -0
- package/dist/runtime/components/ui/EPInputText.vue +33 -0
- package/dist/runtime/components/ui/EPInputText.vue.d.ts +17 -0
- package/dist/runtime/components/ui/EPLinkEditor.d.vue.ts +16 -0
- package/dist/runtime/components/ui/EPLinkEditor.vue +169 -0
- package/dist/runtime/components/ui/EPLinkEditor.vue.d.ts +16 -0
- package/dist/runtime/components/ui/EPRadioButton.d.vue.ts +16 -0
- package/dist/runtime/components/ui/EPRadioButton.vue +34 -0
- package/dist/runtime/components/ui/EPRadioButton.vue.d.ts +16 -0
- package/dist/runtime/components/ui/EPSelect.d.vue.ts +21 -0
- package/dist/runtime/components/ui/EPSelect.vue +73 -0
- package/dist/runtime/components/ui/EPSelect.vue.d.ts +21 -0
- package/dist/runtime/components/ui/EPSlider.d.vue.ts +16 -0
- package/dist/runtime/components/ui/EPSlider.vue +51 -0
- package/dist/runtime/components/ui/EPSlider.vue.d.ts +16 -0
- package/dist/runtime/components/ui/EPSwitch.d.vue.ts +11 -0
- package/dist/runtime/components/ui/EPSwitch.vue +23 -0
- package/dist/runtime/components/ui/EPSwitch.vue.d.ts +11 -0
- package/dist/runtime/components/ui/EPTextarea.d.vue.ts +12 -0
- package/dist/runtime/components/ui/EPTextarea.vue +18 -0
- package/dist/runtime/components/ui/EPTextarea.vue.d.ts +12 -0
- package/dist/runtime/components/ui/EPToast.d.vue.ts +13 -0
- package/dist/runtime/components/ui/EPToast.vue +60 -0
- package/dist/runtime/components/ui/EPToast.vue.d.ts +13 -0
- package/dist/runtime/components/ui/EPTooltip.d.vue.ts +17 -0
- package/dist/runtime/components/ui/EPTooltip.vue +34 -0
- package/dist/runtime/components/ui/EPTooltip.vue.d.ts +17 -0
- package/dist/runtime/components/ui/form-field-context.d.ts +12 -0
- package/dist/runtime/components/ui/form-field-context.js +1 -0
- package/dist/runtime/composables/useEponyme.d.ts +24 -0
- package/dist/runtime/composables/useEponyme.js +63 -0
- package/dist/runtime/composables/useEponymeAuth.d.ts +16 -0
- package/dist/runtime/composables/useEponymeAuth.js +81 -0
- package/dist/runtime/composables/useEponymeCollection.d.ts +27 -0
- package/dist/runtime/composables/useEponymeCollection.js +44 -0
- package/dist/runtime/composables/useEponymeColorPresets.d.ts +5 -0
- package/dist/runtime/composables/useEponymeColorPresets.js +16 -0
- package/dist/runtime/composables/useEponymeConfig.d.ts +1 -0
- package/dist/runtime/composables/useEponymeConfig.js +4 -0
- package/dist/runtime/composables/useEponymeForm.d.ts +35 -0
- package/dist/runtime/composables/useEponymeForm.js +75 -0
- package/dist/runtime/composables/useEponymeHistory.d.ts +26 -0
- package/dist/runtime/composables/useEponymeHistory.js +24 -0
- package/dist/runtime/composables/useEponymeValidation.d.ts +21 -0
- package/dist/runtime/composables/useEponymeValidation.js +34 -0
- package/dist/runtime/fields/array.d.ts +2 -0
- package/dist/runtime/fields/array.js +3 -0
- package/dist/runtime/fields/boolean.d.ts +2 -0
- package/dist/runtime/fields/boolean.js +3 -0
- package/dist/runtime/fields/checkbox-group.d.ts +2 -0
- package/dist/runtime/fields/checkbox-group.js +3 -0
- package/dist/runtime/fields/color.d.ts +2 -0
- package/dist/runtime/fields/color.js +3 -0
- package/dist/runtime/fields/date.d.ts +3 -0
- package/dist/runtime/fields/date.js +6 -0
- package/dist/runtime/fields/email.d.ts +2 -0
- package/dist/runtime/fields/email.js +3 -0
- package/dist/runtime/fields/image.d.ts +2 -0
- package/dist/runtime/fields/image.js +3 -0
- package/dist/runtime/fields/index.d.ts +37 -0
- package/dist/runtime/fields/index.js +19 -0
- package/dist/runtime/fields/number.d.ts +2 -0
- package/dist/runtime/fields/number.js +3 -0
- package/dist/runtime/fields/radio.d.ts +2 -0
- package/dist/runtime/fields/radio.js +3 -0
- package/dist/runtime/fields/rich-text.d.ts +2 -0
- package/dist/runtime/fields/rich-text.js +3 -0
- package/dist/runtime/fields/section.d.ts +2 -0
- package/dist/runtime/fields/section.js +3 -0
- package/dist/runtime/fields/select.d.ts +2 -0
- package/dist/runtime/fields/select.js +3 -0
- package/dist/runtime/fields/slug.d.ts +2 -0
- package/dist/runtime/fields/slug.js +3 -0
- package/dist/runtime/fields/string.d.ts +2 -0
- package/dist/runtime/fields/string.js +3 -0
- package/dist/runtime/fields/tab.d.ts +2 -0
- package/dist/runtime/fields/tab.js +3 -0
- package/dist/runtime/fields/textarea.d.ts +2 -0
- package/dist/runtime/fields/textarea.js +3 -0
- package/dist/runtime/fields/url.d.ts +2 -0
- package/dist/runtime/fields/url.js +3 -0
- package/dist/runtime/middleware/eponyme-auth.d.ts +2 -0
- package/dist/runtime/middleware/eponyme-auth.js +13 -0
- package/dist/runtime/middleware/eponyme-owner.d.ts +2 -0
- package/dist/runtime/middleware/eponyme-owner.js +7 -0
- package/dist/runtime/pages/EponymeChangePasswordPage.d.vue.ts +3 -0
- package/dist/runtime/pages/EponymeChangePasswordPage.vue +118 -0
- package/dist/runtime/pages/EponymeChangePasswordPage.vue.d.ts +3 -0
- package/dist/runtime/pages/EponymeDetailPage.d.vue.ts +3 -0
- package/dist/runtime/pages/EponymeDetailPage.vue +83 -0
- package/dist/runtime/pages/EponymeDetailPage.vue.d.ts +3 -0
- package/dist/runtime/pages/EponymeIndexPage.d.vue.ts +3 -0
- package/dist/runtime/pages/EponymeIndexPage.vue +51 -0
- package/dist/runtime/pages/EponymeIndexPage.vue.d.ts +3 -0
- package/dist/runtime/pages/EponymeLoginPage.d.vue.ts +3 -0
- package/dist/runtime/pages/EponymeLoginPage.vue +102 -0
- package/dist/runtime/pages/EponymeLoginPage.vue.d.ts +3 -0
- package/dist/runtime/pages/EponymeUsersPage.d.vue.ts +3 -0
- package/dist/runtime/pages/EponymeUsersPage.vue +261 -0
- package/dist/runtime/pages/EponymeUsersPage.vue.d.ts +3 -0
- package/dist/runtime/server/api/eponyme/[name].get.d.ts +4 -0
- package/dist/runtime/server/api/eponyme/[name].get.js +20 -0
- package/dist/runtime/server/api/eponyme/[name].patch.d.ts +11 -0
- package/dist/runtime/server/api/eponyme/[name].patch.js +20 -0
- package/dist/runtime/server/api/eponyme-auth/change-password.post.d.ts +4 -0
- package/dist/runtime/server/api/eponyme-auth/change-password.post.js +21 -0
- package/dist/runtime/server/api/eponyme-auth/login.post.d.ts +4 -0
- package/dist/runtime/server/api/eponyme-auth/login.post.js +12 -0
- package/dist/runtime/server/api/eponyme-auth/logout.post.d.ts +4 -0
- package/dist/runtime/server/api/eponyme-auth/logout.post.js +13 -0
- package/dist/runtime/server/api/eponyme-auth/session.get.d.ts +4 -0
- package/dist/runtime/server/api/eponyme-auth/session.get.js +5 -0
- package/dist/runtime/server/api/eponyme-collections/[path].delete.d.ts +4 -0
- package/dist/runtime/server/api/eponyme-collections/[path].delete.js +11 -0
- package/dist/runtime/server/api/eponyme-collections/[path].get.d.ts +4 -0
- package/dist/runtime/server/api/eponyme-collections/[path].get.js +11 -0
- package/dist/runtime/server/api/eponyme-collections/[path].post.d.ts +7 -0
- package/dist/runtime/server/api/eponyme-collections/[path].post.js +16 -0
- package/dist/runtime/server/api/eponyme-forms/[path].delete.d.ts +6 -0
- package/dist/runtime/server/api/eponyme-forms/[path].delete.js +19 -0
- package/dist/runtime/server/api/eponyme-forms/[path].get.d.ts +4 -0
- package/dist/runtime/server/api/eponyme-forms/[path].get.js +22 -0
- package/dist/runtime/server/api/eponyme-forms/[path].post.d.ts +19 -0
- package/dist/runtime/server/api/eponyme-forms/[path].post.js +36 -0
- package/dist/runtime/server/api/eponyme-history/[path].get.d.ts +4 -0
- package/dist/runtime/server/api/eponyme-history/[path].get.js +11 -0
- package/dist/runtime/server/api/eponyme-history/[path].patch.d.ts +2 -0
- package/dist/runtime/server/api/eponyme-history/[path].patch.js +18 -0
- package/dist/runtime/server/api/eponyme-sitemap.get.d.ts +4 -0
- package/dist/runtime/server/api/eponyme-sitemap.get.js +5 -0
- package/dist/runtime/server/api/eponyme-statuses.get.d.ts +4 -0
- package/dist/runtime/server/api/eponyme-statuses.get.js +7 -0
- package/dist/runtime/server/api/eponyme-users/[id]/reset-password.post.d.ts +5 -0
- package/dist/runtime/server/api/eponyme-users/[id]/reset-password.post.js +12 -0
- package/dist/runtime/server/api/eponyme-users/[id].patch.d.ts +4 -0
- package/dist/runtime/server/api/eponyme-users/[id].patch.js +14 -0
- package/dist/runtime/server/api/eponyme-users/index.get.d.ts +4 -0
- package/dist/runtime/server/api/eponyme-users/index.get.js +7 -0
- package/dist/runtime/server/api/eponyme-users/index.post.d.ts +5 -0
- package/dist/runtime/server/api/eponyme-users/index.post.js +13 -0
- package/dist/runtime/server/plugins/eponyme-sync.d.ts +2 -0
- package/dist/runtime/server/plugins/eponyme-sync.js +14 -0
- package/dist/runtime/server/services/eponyme-auth-service.d.ts +2 -0
- package/dist/runtime/server/services/eponyme-auth-service.js +15 -0
- package/dist/runtime/server/services/eponyme-auth-store.d.ts +119 -0
- package/dist/runtime/server/services/eponyme-auth-store.js +227 -0
- package/dist/runtime/server/services/eponyme-form-service.d.ts +3 -0
- package/dist/runtime/server/services/eponyme-form-service.js +8 -0
- package/dist/runtime/server/services/eponyme-form-store.d.ts +100 -0
- package/dist/runtime/server/services/eponyme-form-store.js +109 -0
- package/dist/runtime/server/services/eponyme-service.d.ts +3 -0
- package/dist/runtime/server/services/eponyme-service.js +8 -0
- package/dist/runtime/server/services/eponyme-store.d.ts +202 -0
- package/dist/runtime/server/services/eponyme-store.js +338 -0
- package/dist/runtime/server/utils/auth.d.ts +11 -0
- package/dist/runtime/server/utils/auth.js +41 -0
- package/dist/runtime/server/utils/eponyme-form.d.ts +14 -0
- package/dist/runtime/server/utils/eponyme-form.js +6 -0
- package/dist/runtime/server/utils/eponyme-sitemap.d.ts +7 -0
- package/dist/runtime/server/utils/eponyme-sitemap.js +7 -0
- package/dist/runtime/server/utils/form-route.d.ts +14 -0
- package/dist/runtime/server/utils/form-route.js +14 -0
- package/dist/runtime/server/utils/password.d.ts +4 -0
- package/dist/runtime/server/utils/password.js +30 -0
- package/dist/runtime/types/auth.d.ts +20 -0
- package/dist/runtime/types/auth.js +7 -0
- package/dist/runtime/types/eponyme-config.d.ts +11 -0
- package/dist/runtime/types/eponyme-navigation.d.ts +6 -0
- package/dist/runtime/types/eponyme-navigation.js +0 -0
- package/dist/runtime/types/eponyme.d.ts +87 -0
- package/dist/runtime/types/eponyme.js +0 -0
- package/dist/runtime/types/field.d.ts +203 -0
- package/dist/runtime/types/field.js +0 -0
- package/dist/runtime/types/index.d.ts +3 -0
- package/dist/runtime/types/index.js +3 -0
- package/dist/runtime/types/nuxt.d.ts +11 -0
- package/dist/runtime/utils/as-record.d.ts +1 -0
- package/dist/runtime/utils/as-record.js +3 -0
- package/dist/runtime/utils/build-eponyme-schema.d.ts +22 -0
- package/dist/runtime/utils/build-eponyme-schema.js +42 -0
- package/dist/runtime/utils/changed-paths.d.ts +6 -0
- package/dist/runtime/utils/changed-paths.js +25 -0
- package/dist/runtime/utils/create-default-eponyme-data.d.ts +5 -0
- package/dist/runtime/utils/create-default-eponyme-data.js +12 -0
- package/dist/runtime/utils/field-path.d.ts +14 -0
- package/dist/runtime/utils/field-path.js +23 -0
- package/dist/runtime/utils/get-eponyme-schemas.d.ts +7 -0
- package/dist/runtime/utils/get-eponyme-schemas.js +33 -0
- package/dist/runtime/utils/get-field-default-value.d.ts +4 -0
- package/dist/runtime/utils/get-field-default-value.js +56 -0
- package/dist/runtime/utils/humanize-label.d.ts +6 -0
- package/dist/runtime/utils/humanize-label.js +3 -0
- package/dist/runtime/utils/initials.d.ts +1 -0
- package/dist/runtime/utils/initials.js +3 -0
- package/dist/runtime/utils/is-field-visible.d.ts +2 -0
- package/dist/runtime/utils/is-field-visible.js +14 -0
- package/dist/runtime/utils/normalize-hex-color.d.ts +8 -0
- package/dist/runtime/utils/normalize-hex-color.js +11 -0
- package/dist/runtime/utils/normalize-slug.d.ts +1 -0
- package/dist/runtime/utils/normalize-slug.js +4 -0
- package/dist/runtime/utils/preview.d.ts +30 -0
- package/dist/runtime/utils/preview.js +30 -0
- package/dist/runtime/utils/validate-eponyme-data.d.ts +10 -0
- package/dist/runtime/utils/validate-eponyme-data.js +204 -0
- package/dist/types.d.mts +11 -0
- package/package.json +96 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
export { collection, defineEponymeConfig, form } from './eponyme.mjs';
|
|
3
|
+
export { field, today } from '../dist/runtime/fields/index.js';
|
|
4
|
+
export { EPONYME_ROLES, canEditEponyme, isEponymeRole } from '../dist/runtime/types/auth.js';
|
|
5
|
+
export { ArrayFieldDefinition, ArrayFieldOptions, ArrayItemDefinition, ArrayItemFieldDefinition, ArrayItemSchema, ArrayItemValue, BooleanFieldDefinition, BooleanFieldOptions, CheckboxGroupFieldDefinition, CheckboxGroupFieldOptions, CollectionSlugField, ColorFieldDefinition, ColorFieldOptions, DateFieldDefinition, DateFieldOptions, EmailFieldDefinition, EmailFieldOptions, EponymeAuthSession, EponymeAuthUser, EponymeCollectionDataByName, EponymeCollectionDefinition, EponymeCollectionDefinitionBase, EponymeCollectionName, EponymeCollectionOptions, EponymeConfig, EponymeData, EponymeDataByName, EponymeFormDataByName, EponymeFormDefinition, EponymeFormDefinitionBase, EponymeFormFieldDefinition, EponymeFormMode, EponymeFormName, EponymeFormOptions, EponymeFormSchema, EponymeManagedUserResult, EponymeName, EponymeRole, EponymeSchema, FieldDefinition, FieldValidationResult, FieldValidator, FieldVisibilityCondition, ImageFieldDefinition, ImageFieldOptions, NumberFieldDefinition, NumberFieldOptions, RadioFieldDefinition, RadioFieldOptions, RichTextFieldDefinition, RichTextFieldOptions, SectionFieldDefinition, SectionFieldOptions, SectionItemFieldDefinition, SectionItemValue, SectionSchema, SectionValue, SelectFieldDefinition, SelectFieldOptions, SelectOption, SlugFieldDefinition, SlugFieldOptions, StringFieldDefinition, StringFieldOptions, TabDefinition, TabFieldDefinition, TabFieldOptions, TabsSchema, TabsValue, TextareaFieldDefinition, TextareaFieldOptions, TodayDateDefault, UrlFieldDefinition, UrlFieldOptions, UrlType, UrlValue } from '../dist/runtime/types/index.js';
|
|
6
|
+
|
|
7
|
+
interface ModuleOptions {
|
|
8
|
+
dashboardPath?: string;
|
|
9
|
+
/** Public routes used for previews and sitemap entries. Collections use a `:slug` placeholder. */
|
|
10
|
+
previewPaths?: Record<string, string>;
|
|
11
|
+
/** Path to a server module exporting an already initialized PrismaClient. */
|
|
12
|
+
prismaClient: string;
|
|
13
|
+
/** Default palette offered by every color field; a field can override it with its own `presets`. */
|
|
14
|
+
colorPresets?: Array<string | {
|
|
15
|
+
label: string;
|
|
16
|
+
value: string;
|
|
17
|
+
}>;
|
|
18
|
+
auth?: {
|
|
19
|
+
/** Fixed lifetime of an authenticated session. */
|
|
20
|
+
sessionDurationDays?: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
24
|
+
|
|
25
|
+
export { _default as default };
|
|
26
|
+
export type { ModuleOptions };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { defineNuxtModule, useLogger, createResolver, installModule, findPath, addImports, addImportsDir, addServerImports, addRouteMiddleware, addTypeTemplate, addServerHandler, addServerPlugin } from '@nuxt/kit';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const module$1 = defineNuxtModule({
|
|
7
|
+
meta: {
|
|
8
|
+
name: "@karibsen/eponyme",
|
|
9
|
+
configKey: "eponyme"
|
|
10
|
+
},
|
|
11
|
+
defaults: {
|
|
12
|
+
dashboardPath: "/__eponyme",
|
|
13
|
+
previewPaths: {},
|
|
14
|
+
prismaClient: "",
|
|
15
|
+
colorPresets: [],
|
|
16
|
+
auth: {
|
|
17
|
+
sessionDurationDays: 7
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
async setup(options, nuxt) {
|
|
21
|
+
const logger = useLogger("eponyme");
|
|
22
|
+
const resolver = createResolver(import.meta.url);
|
|
23
|
+
const dashboardPath = `/${(options.dashboardPath || "__eponyme").replace(/^\/+|\/+$/g, "")}`;
|
|
24
|
+
const publicRuntimeConfig = nuxt.options.runtimeConfig.public;
|
|
25
|
+
publicRuntimeConfig.eponyme = {
|
|
26
|
+
previewPaths: options.previewPaths ?? {},
|
|
27
|
+
dashboardPath,
|
|
28
|
+
colorPresets: options.colorPresets ?? []
|
|
29
|
+
};
|
|
30
|
+
nuxt.options.runtimeConfig.eponymeAuth = {
|
|
31
|
+
sessionDurationDays: options.auth?.sessionDurationDays ?? 7
|
|
32
|
+
};
|
|
33
|
+
await installModule("@nuxt/icon", {
|
|
34
|
+
serverBundle: { collections: ["mingcute"] },
|
|
35
|
+
clientBundle: {
|
|
36
|
+
// Explicit list: an icon missing here is not bundled and renders blank in production.
|
|
37
|
+
icons: [
|
|
38
|
+
"mingcute:history-anticlockwise-line",
|
|
39
|
+
"mingcute:eye-2-line",
|
|
40
|
+
"mingcute:layout-leftbar-close-line",
|
|
41
|
+
"mingcute:layout-leftbar-open-line",
|
|
42
|
+
"mingcute:down-small-line",
|
|
43
|
+
"mingcute:search-line",
|
|
44
|
+
"mingcute:close-line",
|
|
45
|
+
"mingcute:arrow-right-line",
|
|
46
|
+
"mingcute:align-arrow-right-line",
|
|
47
|
+
"mingcute:link-2-line",
|
|
48
|
+
"mingcute:settings-3-line",
|
|
49
|
+
"mingcute:refresh-2-line",
|
|
50
|
+
"mingcute:external-link-line",
|
|
51
|
+
"mingcute:paragraph-line",
|
|
52
|
+
"mingcute:heading-2-line",
|
|
53
|
+
"mingcute:heading-3-line",
|
|
54
|
+
"mingcute:bold-line",
|
|
55
|
+
"mingcute:italic-line",
|
|
56
|
+
"mingcute:strikethrough-line",
|
|
57
|
+
"mingcute:link-line",
|
|
58
|
+
"mingcute:pic-line",
|
|
59
|
+
"mingcute:list-check-line",
|
|
60
|
+
"mingcute:list-ordered-line",
|
|
61
|
+
"mingcute:quote-left-line",
|
|
62
|
+
"mingcute:back-2-line",
|
|
63
|
+
"mingcute:forward-2-line"
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
}, nuxt);
|
|
67
|
+
const preparing = Boolean(nuxt.options._prepare);
|
|
68
|
+
if (!options.prismaClient) {
|
|
69
|
+
if (!preparing) throw new Error(`${pc.red("[Eponyme]")} eponyme.prismaClient is required.`);
|
|
70
|
+
logger.info("No prismaClient configured, skipping wiring (prepare).");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const prismaPath = await findPath(resolve(nuxt.options.rootDir, options.prismaClient));
|
|
74
|
+
if (!prismaPath)
|
|
75
|
+
throw new Error(`${pc.red("[Eponyme]")} Prisma client module not found: ${options.prismaClient}`);
|
|
76
|
+
addImports([
|
|
77
|
+
{ name: "defineEponymeConfig", from: resolver.resolve("./config/config") },
|
|
78
|
+
{ name: "collection", from: resolver.resolve("./config/collection") },
|
|
79
|
+
{ name: "form", from: resolver.resolve("./config/form") },
|
|
80
|
+
{ name: "field", from: resolver.resolve("./runtime/fields") },
|
|
81
|
+
{ name: "today", from: resolver.resolve("./runtime/fields") }
|
|
82
|
+
]);
|
|
83
|
+
addImportsDir(resolver.resolve("./runtime/composables"));
|
|
84
|
+
addServerImports([
|
|
85
|
+
{ name: "getEponymeSitemapEntries", from: resolver.resolve("./runtime/server/utils/eponyme-sitemap") },
|
|
86
|
+
// A `custom` form posts to the host application's own route, so this is what
|
|
87
|
+
// keeps server-side validation as the security boundary.
|
|
88
|
+
{ name: "validateEponymeForm", from: resolver.resolve("./runtime/server/utils/eponyme-form") }
|
|
89
|
+
]);
|
|
90
|
+
addRouteMiddleware({
|
|
91
|
+
name: "eponyme-auth",
|
|
92
|
+
path: resolver.resolve("./runtime/middleware/eponyme-auth")
|
|
93
|
+
});
|
|
94
|
+
addRouteMiddleware({
|
|
95
|
+
name: "eponyme-owner",
|
|
96
|
+
path: resolver.resolve("./runtime/middleware/eponyme-owner")
|
|
97
|
+
});
|
|
98
|
+
nuxt.options.build.transpile.push("reka-ui");
|
|
99
|
+
nuxt.options.css.push(resolver.resolve("./runtime/assets/dashboard.css"));
|
|
100
|
+
nuxt.hook("vite:extendConfig", (viteConfig) => {
|
|
101
|
+
const plugins = viteConfig.plugins;
|
|
102
|
+
plugins?.push(tailwindcss());
|
|
103
|
+
});
|
|
104
|
+
const configPath = await findPath(resolve(nuxt.options.rootDir, "eponyme.config.ts"));
|
|
105
|
+
if (!configPath) {
|
|
106
|
+
if (!preparing) throw new Error(`${pc.red("[Eponyme]")} eponyme.config.ts not found at the project root.`);
|
|
107
|
+
logger.info("No eponyme.config.ts found, skipping wiring (prepare).");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
nuxt.options.alias["#eponyme/config"] = configPath;
|
|
111
|
+
nuxt.options.alias["#eponyme/prisma"] = prismaPath;
|
|
112
|
+
addTypeTemplate({
|
|
113
|
+
filename: "types/eponyme-config.d.ts",
|
|
114
|
+
getContents: () => `declare module '#eponyme/config' {
|
|
115
|
+
const config: typeof import(${JSON.stringify(configPath)})['default']
|
|
116
|
+
export default config
|
|
117
|
+
}
|
|
118
|
+
`
|
|
119
|
+
});
|
|
120
|
+
addServerHandler({ route: "/api/eponyme/**", handler: resolver.resolve("./runtime/server/api/eponyme/[name].get") });
|
|
121
|
+
addServerHandler({ route: "/api/eponyme/**", method: "patch", handler: resolver.resolve("./runtime/server/api/eponyme/[name].patch") });
|
|
122
|
+
addServerHandler({ route: "/api/eponyme-statuses", handler: resolver.resolve("./runtime/server/api/eponyme-statuses.get") });
|
|
123
|
+
addServerHandler({ route: "/api/eponyme-sitemap", handler: resolver.resolve("./runtime/server/api/eponyme-sitemap.get") });
|
|
124
|
+
addServerHandler({ route: "/api/eponyme-history/**", handler: resolver.resolve("./runtime/server/api/eponyme-history/[path].get") });
|
|
125
|
+
addServerHandler({ route: "/api/eponyme-history/**", method: "patch", handler: resolver.resolve("./runtime/server/api/eponyme-history/[path].patch") });
|
|
126
|
+
addServerHandler({ route: "/api/eponyme-collections/**", handler: resolver.resolve("./runtime/server/api/eponyme-collections/[path].get") });
|
|
127
|
+
addServerHandler({ route: "/api/eponyme-collections/**", method: "post", handler: resolver.resolve("./runtime/server/api/eponyme-collections/[path].post") });
|
|
128
|
+
addServerHandler({ route: "/api/eponyme-collections/**", method: "delete", handler: resolver.resolve("./runtime/server/api/eponyme-collections/[path].delete") });
|
|
129
|
+
addServerHandler({ route: "/api/eponyme-forms/**", handler: resolver.resolve("./runtime/server/api/eponyme-forms/[path].get") });
|
|
130
|
+
addServerHandler({ route: "/api/eponyme-forms/**", method: "post", handler: resolver.resolve("./runtime/server/api/eponyme-forms/[path].post") });
|
|
131
|
+
addServerHandler({ route: "/api/eponyme-forms/**", method: "delete", handler: resolver.resolve("./runtime/server/api/eponyme-forms/[path].delete") });
|
|
132
|
+
addServerHandler({ route: "/api/eponyme-auth/session", handler: resolver.resolve("./runtime/server/api/eponyme-auth/session.get") });
|
|
133
|
+
addServerHandler({ route: "/api/eponyme-auth/login", method: "post", handler: resolver.resolve("./runtime/server/api/eponyme-auth/login.post") });
|
|
134
|
+
addServerHandler({ route: "/api/eponyme-auth/logout", method: "post", handler: resolver.resolve("./runtime/server/api/eponyme-auth/logout.post") });
|
|
135
|
+
addServerHandler({ route: "/api/eponyme-auth/change-password", method: "post", handler: resolver.resolve("./runtime/server/api/eponyme-auth/change-password.post") });
|
|
136
|
+
addServerHandler({ route: "/api/eponyme-users", handler: resolver.resolve("./runtime/server/api/eponyme-users/index.get") });
|
|
137
|
+
addServerHandler({ route: "/api/eponyme-users", method: "post", handler: resolver.resolve("./runtime/server/api/eponyme-users/index.post") });
|
|
138
|
+
addServerHandler({ route: "/api/eponyme-users/:id", method: "patch", handler: resolver.resolve("./runtime/server/api/eponyme-users/[id].patch") });
|
|
139
|
+
addServerHandler({ route: "/api/eponyme-users/:id/reset-password", method: "post", handler: resolver.resolve("./runtime/server/api/eponyme-users/[id]/reset-password.post") });
|
|
140
|
+
addServerPlugin(resolver.resolve("./runtime/server/plugins/eponyme-sync"));
|
|
141
|
+
nuxt.hook("pages:extend", (pages) => {
|
|
142
|
+
pages.push(
|
|
143
|
+
{ name: "eponyme-login", path: `${dashboardPath}/login`, file: resolver.resolve("./runtime/pages/EponymeLoginPage.vue") },
|
|
144
|
+
{ name: "eponyme-change-password", path: `${dashboardPath}/change-password`, file: resolver.resolve("./runtime/pages/EponymeChangePasswordPage.vue"), meta: { middleware: ["eponyme-auth"] } },
|
|
145
|
+
{ name: "eponyme-users", path: `${dashboardPath}/users`, file: resolver.resolve("./runtime/pages/EponymeUsersPage.vue"), meta: { middleware: ["eponyme-auth", "eponyme-owner"] } },
|
|
146
|
+
{ name: "eponyme-index", path: dashboardPath, file: resolver.resolve("./runtime/pages/EponymeIndexPage.vue"), meta: { middleware: ["eponyme-auth"] } },
|
|
147
|
+
{ name: "eponyme-detail", path: `${dashboardPath}/:eponyme(.*)*`, file: resolver.resolve("./runtime/pages/EponymeDetailPage.vue"), meta: { middleware: ["eponyme-auth"] } }
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
logger.debug(`config loaded: ${configPath}`);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
export { module$1 as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import "tailwindcss/theme" layer(theme) prefix(ep);@import "tailwindcss/preflight" layer(base);@import "tailwindcss/utilities" layer(utilities) prefix(ep);@source "../components";@source "../pages";@theme{--font-sans:"SF Pro Text",Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;--color-theme-ep:#1c1c1c;--color-border-ep:#343434;--color-selected-ep:#262626;--color-text-ep:#e7e7e7;--color-muted-ep:#8d8d8d;--color-success-ep:#72d39a;--color-warning-ep:#e6b85c;--color-danger-ep:#ff8585}:root.dark{--color-eponyme-50:#fafafa;--color-eponyme-100:#f5f5f5;--color-eponyme-200:#e6e6e6;--color-eponyme-300:#d3d3d3;--color-eponyme-400:#a3a3a3;--color-eponyme-500:#727272;--color-eponyme-600:#535353;--color-eponyme-700:#404040;--color-eponyme-800:#272727;--color-eponyme-900:#1c1c1c;--color-eponyme-950:#0b0b0b}@layer base{.eponyme-portal,.eponyme-root{font-family:var(--ep-font-sans)}:where(.eponyme-root,.eponyme-portal),:where(.eponyme-root,.eponyme-portal) *,:where(.eponyme-root,.eponyme-portal) :after,:where(.eponyme-root,.eponyme-portal) :before{border:0 solid;box-sizing:border-box;margin:0;padding:0}}.eponyme-date-input{color-scheme:dark}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EponymeCollectionDefinitionBase } from '../../types/index.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
basePath: string;
|
|
4
|
+
name: string;
|
|
5
|
+
definition: EponymeCollectionDefinitionBase;
|
|
6
|
+
};
|
|
7
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
8
|
+
declare const _default: typeof __VLS_export;
|
|
9
|
+
export default _default;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useAsyncData, useRequestFetch, useRoute, useRouter, useState } from "#app";
|
|
3
|
+
import { computed, ref, watch } from "vue";
|
|
4
|
+
import { normalizeSlug } from "../../utils/normalize-slug";
|
|
5
|
+
import { useEponymeAuth } from "../../composables/useEponymeAuth";
|
|
6
|
+
import EPBadge from "../ui/EPBadge.vue";
|
|
7
|
+
import EPButton from "../ui/EPButton.vue";
|
|
8
|
+
import EPDialog from "../ui/EPDialog.vue";
|
|
9
|
+
import EPFormField from "../ui/EPFormField.vue";
|
|
10
|
+
import EPInputText from "../ui/EPInputText.vue";
|
|
11
|
+
const props = defineProps({
|
|
12
|
+
basePath: { type: String, required: true },
|
|
13
|
+
name: { type: String, required: true },
|
|
14
|
+
definition: { type: Object, required: true }
|
|
15
|
+
});
|
|
16
|
+
const route = useRoute();
|
|
17
|
+
const router = useRouter();
|
|
18
|
+
const requestFetch = useRequestFetch();
|
|
19
|
+
const createOpen = ref(route.query.create === "1");
|
|
20
|
+
const creating = ref(false);
|
|
21
|
+
const title = ref("");
|
|
22
|
+
const slug = ref("");
|
|
23
|
+
const slugTouched = ref(false);
|
|
24
|
+
const errors = ref({});
|
|
25
|
+
const collectionEntries = useState("eponyme:collection-entries", () => ({}));
|
|
26
|
+
const endpoint = computed(() => `/api/eponyme-collections/${props.name}`);
|
|
27
|
+
const auth = useEponymeAuth();
|
|
28
|
+
const { data: response, pending, refresh } = useAsyncData(
|
|
29
|
+
`eponyme:collection:${props.name}`,
|
|
30
|
+
() => requestFetch(endpoint.value, { query: { version: "draft" } })
|
|
31
|
+
);
|
|
32
|
+
const entries = computed(() => response.value?.entries ?? []);
|
|
33
|
+
const label = computed(() => props.definition.label || humanize(props.name.split("/").at(-1) || props.name));
|
|
34
|
+
watch(title, (value) => {
|
|
35
|
+
if (!slugTouched.value) slug.value = normalizeSlug(value);
|
|
36
|
+
});
|
|
37
|
+
watch(() => route.query.create, (value) => createOpen.value = value === "1");
|
|
38
|
+
watch(entries, (value) => collectionEntries.value = { ...collectionEntries.value, [props.name]: value }, { immediate: true });
|
|
39
|
+
function humanize(value) {
|
|
40
|
+
return value.replace(/[-_]/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
41
|
+
}
|
|
42
|
+
function truncateTitle(value, maxLength = 48) {
|
|
43
|
+
const characters = [...value];
|
|
44
|
+
if (characters.length <= maxLength) return value;
|
|
45
|
+
return `${characters.slice(0, maxLength - 1).join("")}\u2026`;
|
|
46
|
+
}
|
|
47
|
+
function setSlug(value) {
|
|
48
|
+
slugTouched.value = true;
|
|
49
|
+
slug.value = normalizeSlug(String(value));
|
|
50
|
+
}
|
|
51
|
+
function resetCreateForm() {
|
|
52
|
+
title.value = "";
|
|
53
|
+
slug.value = "";
|
|
54
|
+
slugTouched.value = false;
|
|
55
|
+
errors.value = {};
|
|
56
|
+
}
|
|
57
|
+
async function setCreateOpen(open) {
|
|
58
|
+
createOpen.value = open;
|
|
59
|
+
if (!open) {
|
|
60
|
+
resetCreateForm();
|
|
61
|
+
if (route.query.create === "1") {
|
|
62
|
+
const query = { ...route.query };
|
|
63
|
+
delete query.create;
|
|
64
|
+
await router.replace({ query });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function createEntry() {
|
|
69
|
+
creating.value = true;
|
|
70
|
+
errors.value = {};
|
|
71
|
+
try {
|
|
72
|
+
const result = await requestFetch(endpoint.value, {
|
|
73
|
+
method: "POST",
|
|
74
|
+
body: {
|
|
75
|
+
[props.definition.titleField]: title.value,
|
|
76
|
+
[props.definition.slugField]: slug.value
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
await refresh();
|
|
80
|
+
await setCreateOpen(false);
|
|
81
|
+
await router.push(`${props.basePath}/${props.name}/${result.slug}`);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
const fetchError = error;
|
|
84
|
+
errors.value = fetchError.data?.errors ?? { _form: ["Unable to create this entry."] };
|
|
85
|
+
} finally {
|
|
86
|
+
creating.value = false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async function deleteEntry(entry) {
|
|
90
|
+
if (!confirm(`Delete \u201C${entry.title}\u201D? This cannot be undone.`)) return;
|
|
91
|
+
await requestFetch(`${endpoint.value}/${entry.slug}`, { method: "DELETE" });
|
|
92
|
+
await refresh();
|
|
93
|
+
}
|
|
94
|
+
function formatDate(value) {
|
|
95
|
+
return value ? new Intl.DateTimeFormat(void 0, { dateStyle: "medium", timeStyle: "short" }).format(new Date(value)) : "";
|
|
96
|
+
}
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<template>
|
|
100
|
+
<section class="ep:mx-auto ep:w-full ep:max-w-3xl ep:px-6 ep:py-8 ep:md:px-10 ep:md:py-12">
|
|
101
|
+
<header class="ep:flex ep:flex-wrap ep:items-start ep:justify-between ep:gap-4">
|
|
102
|
+
<div>
|
|
103
|
+
<p class="ep:m-0 ep:text-[11px] ep:font-semibold ep:tracking-widest ep:text-muted-ep ep:uppercase">
|
|
104
|
+
Collection
|
|
105
|
+
</p>
|
|
106
|
+
<h1 class="ep:mt-2 ep:mb-0 ep:text-3xl ep:font-semibold ep:tracking-tight ep:text-white">
|
|
107
|
+
{{ label }}
|
|
108
|
+
</h1>
|
|
109
|
+
<p
|
|
110
|
+
v-if="definition.description"
|
|
111
|
+
class="ep:mt-2 ep:mb-0 ep:text-sm ep:text-muted-ep"
|
|
112
|
+
>
|
|
113
|
+
{{ definition.description }}
|
|
114
|
+
</p>
|
|
115
|
+
</div>
|
|
116
|
+
<EPButton
|
|
117
|
+
v-if="auth.canEdit.value"
|
|
118
|
+
variant="primary"
|
|
119
|
+
@click="createOpen = true"
|
|
120
|
+
>
|
|
121
|
+
+ New {{ humanize(name.split("/").at(-1) || "entry").replace(/s$/, "") }}
|
|
122
|
+
</EPButton>
|
|
123
|
+
</header>
|
|
124
|
+
|
|
125
|
+
<p
|
|
126
|
+
v-if="pending"
|
|
127
|
+
class="ep:mt-8 ep:text-sm ep:text-muted-ep"
|
|
128
|
+
>
|
|
129
|
+
Loading…
|
|
130
|
+
</p>
|
|
131
|
+
<div
|
|
132
|
+
v-else-if="entries.length"
|
|
133
|
+
class="ep:mt-8 ep:grid ep:gap-3"
|
|
134
|
+
>
|
|
135
|
+
<article
|
|
136
|
+
v-for="entry in entries"
|
|
137
|
+
:key="entry.slug"
|
|
138
|
+
class="ep:group ep:flex ep:items-center ep:justify-between ep:gap-4 ep:rounded-xl ep:bg-selected-ep/50 ep:p-4"
|
|
139
|
+
>
|
|
140
|
+
<NuxtLink
|
|
141
|
+
:to="`${basePath}/${name}/${entry.slug}`"
|
|
142
|
+
:title="entry.title"
|
|
143
|
+
class="ep:min-w-0 ep:flex-1 ep:no-underline"
|
|
144
|
+
>
|
|
145
|
+
<span class="ep:flex ep:min-w-0 ep:items-center ep:gap-2">
|
|
146
|
+
<span class="ep:min-w-0 ep:flex-1 ep:overflow-hidden ep:text-ellipsis ep:whitespace-nowrap ep:text-sm ep:font-semibold ep:text-white">{{ truncateTitle(entry.title) }}</span>
|
|
147
|
+
<EPBadge
|
|
148
|
+
class="ep:shrink-0"
|
|
149
|
+
:variant="entry.status === 'draft' ? 'warning' : 'success'"
|
|
150
|
+
>{{ entry.status }}</EPBadge>
|
|
151
|
+
</span>
|
|
152
|
+
<span class="ep:mt-1 ep:block ep:text-xs ep:text-muted-ep">/{{ entry.slug }}<template v-if="entry.updatedAt"> · {{ formatDate(entry.updatedAt) }}</template></span>
|
|
153
|
+
</NuxtLink>
|
|
154
|
+
<EPButton
|
|
155
|
+
v-if="auth.canEdit.value"
|
|
156
|
+
size="sm"
|
|
157
|
+
variant="danger"
|
|
158
|
+
class="ep:opacity-0 ep:group-hover:opacity-100 ep:focus-visible:opacity-100"
|
|
159
|
+
@click="deleteEntry(entry)"
|
|
160
|
+
>
|
|
161
|
+
Delete
|
|
162
|
+
</EPButton>
|
|
163
|
+
</article>
|
|
164
|
+
</div>
|
|
165
|
+
<div
|
|
166
|
+
v-else
|
|
167
|
+
class="ep:mt-8 ep:rounded-xl ep:border ep:border-dashed ep:border-border-ep ep:p-8 ep:text-center"
|
|
168
|
+
>
|
|
169
|
+
<p class="ep:m-0 ep:text-sm ep:text-muted-ep">
|
|
170
|
+
No entries yet.
|
|
171
|
+
</p>
|
|
172
|
+
<EPButton
|
|
173
|
+
v-if="auth.canEdit.value"
|
|
174
|
+
class="ep:mt-4"
|
|
175
|
+
@click="createOpen = true"
|
|
176
|
+
>
|
|
177
|
+
Create the first one
|
|
178
|
+
</EPButton>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<EPDialog
|
|
182
|
+
:open="createOpen && auth.canEdit.value"
|
|
183
|
+
:title="`New ${label.replace(/s$/, '')}`"
|
|
184
|
+
description="Give the entry a title and a permanent URL slug."
|
|
185
|
+
@update:open="setCreateOpen"
|
|
186
|
+
>
|
|
187
|
+
<form
|
|
188
|
+
class="ep:grid ep:gap-4"
|
|
189
|
+
@submit.prevent="createEntry"
|
|
190
|
+
>
|
|
191
|
+
<EPFormField
|
|
192
|
+
:id="`collection-${name}-title`"
|
|
193
|
+
:label="humanize(definition.titleField)"
|
|
194
|
+
required
|
|
195
|
+
:errors="errors[definition.titleField]"
|
|
196
|
+
>
|
|
197
|
+
<EPInputText
|
|
198
|
+
:id="`collection-${name}-title`"
|
|
199
|
+
v-model="title"
|
|
200
|
+
autofocus
|
|
201
|
+
:invalid="Boolean(errors[definition.titleField]?.length)"
|
|
202
|
+
/>
|
|
203
|
+
</EPFormField>
|
|
204
|
+
<EPFormField
|
|
205
|
+
:id="`collection-${name}-slug`"
|
|
206
|
+
:label="humanize(definition.slugField)"
|
|
207
|
+
required
|
|
208
|
+
:errors="errors[definition.slugField]"
|
|
209
|
+
>
|
|
210
|
+
<EPInputText
|
|
211
|
+
:id="`collection-${name}-slug`"
|
|
212
|
+
:model-value="slug"
|
|
213
|
+
:invalid="Boolean(errors[definition.slugField]?.length)"
|
|
214
|
+
@update:model-value="setSlug"
|
|
215
|
+
/>
|
|
216
|
+
</EPFormField>
|
|
217
|
+
<p
|
|
218
|
+
v-if="errors._form"
|
|
219
|
+
class="ep:m-0 ep:text-xs ep:text-danger-ep"
|
|
220
|
+
>
|
|
221
|
+
{{ errors._form.join(" ") }}
|
|
222
|
+
</p>
|
|
223
|
+
<div class="ep:flex ep:justify-end ep:gap-2">
|
|
224
|
+
<EPButton @click="setCreateOpen(false)">
|
|
225
|
+
Cancel
|
|
226
|
+
</EPButton>
|
|
227
|
+
<EPButton
|
|
228
|
+
type="submit"
|
|
229
|
+
variant="primary"
|
|
230
|
+
:loading="creating"
|
|
231
|
+
>
|
|
232
|
+
Create
|
|
233
|
+
</EPButton>
|
|
234
|
+
</div>
|
|
235
|
+
</form>
|
|
236
|
+
</EPDialog>
|
|
237
|
+
</section>
|
|
238
|
+
</template>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EponymeCollectionDefinitionBase } from '../../types/index.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
basePath: string;
|
|
4
|
+
name: string;
|
|
5
|
+
definition: EponymeCollectionDefinitionBase;
|
|
6
|
+
};
|
|
7
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
8
|
+
declare const _default: typeof __VLS_export;
|
|
9
|
+
export default _default;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
value: string;
|
|
3
|
+
label?: string;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
|
+
declare const _default: typeof __VLS_export;
|
|
7
|
+
export default _default;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useClipboard } from "@vueuse/core";
|
|
3
|
+
import { computed } from "vue";
|
|
4
|
+
import EPButton from "../ui/EPButton.vue";
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
value: { type: String, required: true },
|
|
7
|
+
label: { type: String, required: false, default: "Copy" }
|
|
8
|
+
});
|
|
9
|
+
const source = computed(() => props.value);
|
|
10
|
+
const { copy, copied, isSupported } = useClipboard({ source });
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<div class="ep:flex ep:items-center ep:gap-2 ep:rounded-xl ep:bg-selected-ep ep:p-2">
|
|
15
|
+
<code class="ep:min-w-0 ep:flex-1 ep:break-all ep:px-1 ep:font-mono ep:text-sm ep:text-white">
|
|
16
|
+
{{ value }}
|
|
17
|
+
</code>
|
|
18
|
+
<EPButton
|
|
19
|
+
size="sm"
|
|
20
|
+
variant="secondary"
|
|
21
|
+
:disabled="!isSupported"
|
|
22
|
+
:aria-label="label"
|
|
23
|
+
@click="copy()"
|
|
24
|
+
>
|
|
25
|
+
{{ copied ? "Copied" : label }}
|
|
26
|
+
</EPButton>
|
|
27
|
+
</div>
|
|
28
|
+
</template>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
value: string;
|
|
3
|
+
label?: string;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
|
+
declare const _default: typeof __VLS_export;
|
|
7
|
+
export default _default;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
basePath: string;
|
|
3
|
+
eponyme: string;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
|
+
declare const _default: typeof __VLS_export;
|
|
7
|
+
export default _default;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, ref, watch } from "vue";
|
|
3
|
+
const props = defineProps({
|
|
4
|
+
basePath: { type: String, required: true },
|
|
5
|
+
eponyme: { type: String, required: true }
|
|
6
|
+
});
|
|
7
|
+
const parts = computed(() => props.eponyme.split("/"));
|
|
8
|
+
const folder = computed(() => parts.value.slice(0, -1).join("/"));
|
|
9
|
+
const eponymeLabel = computed(() => label(parts.value[parts.value.length - 1] ?? ""));
|
|
10
|
+
const breadcrumbs = computed(() => parts.value.slice(0, -1).map((part, index) => ({
|
|
11
|
+
label: label(part),
|
|
12
|
+
path: parts.value.slice(0, index + 1).join("/")
|
|
13
|
+
})));
|
|
14
|
+
const hiddenBreadcrumbs = computed(() => breadcrumbs.value.length > 3 ? breadcrumbs.value.slice(1, -1) : []);
|
|
15
|
+
const breadcrumbMenuAtBottom = ref(false);
|
|
16
|
+
const visibleBreadcrumbs = computed(() => {
|
|
17
|
+
if (!hiddenBreadcrumbs.value.length) return breadcrumbs.value.map((breadcrumb) => ({ kind: "link", ...breadcrumb }));
|
|
18
|
+
return [
|
|
19
|
+
{ kind: "link", ...breadcrumbs.value[0] },
|
|
20
|
+
{ kind: "collapsed", label: "Hidden folders", path: "__collapsed__" },
|
|
21
|
+
{ kind: "link", ...breadcrumbs.value.at(-1) }
|
|
22
|
+
];
|
|
23
|
+
});
|
|
24
|
+
function label(name) {
|
|
25
|
+
return name.replace(/[-_]/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
26
|
+
}
|
|
27
|
+
function middleEllipsis(value, maxLength = 28) {
|
|
28
|
+
const characters = [...value];
|
|
29
|
+
if (characters.length <= maxLength) return value;
|
|
30
|
+
const available = maxLength - 1;
|
|
31
|
+
const startLength = Math.ceil(available / 2);
|
|
32
|
+
const endLength = Math.floor(available / 2);
|
|
33
|
+
return `${characters.slice(0, startLength).join("")}\u2026${characters.slice(-endLength).join("")}`;
|
|
34
|
+
}
|
|
35
|
+
function updateBreadcrumbMenuScroll(event) {
|
|
36
|
+
const menu = event.currentTarget;
|
|
37
|
+
if (!menu) return;
|
|
38
|
+
const remainingScroll = menu.scrollHeight - menu.clientHeight - menu.scrollTop;
|
|
39
|
+
breadcrumbMenuAtBottom.value = remainingScroll <= 2;
|
|
40
|
+
}
|
|
41
|
+
watch(() => hiddenBreadcrumbs.value.map((breadcrumb) => breadcrumb.path).join("|"), () => {
|
|
42
|
+
breadcrumbMenuAtBottom.value = false;
|
|
43
|
+
});
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<template>
|
|
47
|
+
<nav
|
|
48
|
+
v-if="folder"
|
|
49
|
+
class="ep:relative ep:z-50 ep:flex ep:min-h-14 ep:min-w-0 ep:items-center ep:overflow-visible ep:border-b ep:border-border-ep ep:bg-theme-ep/95 ep:px-3 ep:text-xs ep:backdrop-blur"
|
|
50
|
+
aria-label="Eponyme navigation"
|
|
51
|
+
>
|
|
52
|
+
<div class="eponyme-mobile-breadcrumbs ep:flex ep:min-w-0 ep:flex-1 ep:touch-pan-x ep:items-center ep:overflow-x-auto ep:overscroll-x-contain ep:sm:hidden">
|
|
53
|
+
<template
|
|
54
|
+
v-for="breadcrumb in breadcrumbs"
|
|
55
|
+
:key="breadcrumb.path"
|
|
56
|
+
>
|
|
57
|
+
<NuxtLink
|
|
58
|
+
:to="`${basePath}/${breadcrumb.path}`"
|
|
59
|
+
:title="breadcrumb.label"
|
|
60
|
+
class="ep:max-w-40 ep:shrink-0 ep:overflow-hidden ep:text-ellipsis ep:whitespace-nowrap ep:rounded-md ep:px-2 ep:py-1.5 ep:font-medium ep:text-muted-ep ep:no-underline"
|
|
61
|
+
>
|
|
62
|
+
{{ middleEllipsis(breadcrumb.label) }}
|
|
63
|
+
</NuxtLink>
|
|
64
|
+
<span
|
|
65
|
+
class="ep:mx-1 ep:shrink-0 ep:text-muted-ep"
|
|
66
|
+
aria-hidden="true"
|
|
67
|
+
>/</span>
|
|
68
|
+
</template>
|
|
69
|
+
<span
|
|
70
|
+
:title="eponymeLabel"
|
|
71
|
+
class="ep:shrink-0 ep:whitespace-nowrap ep:px-2 ep:font-medium ep:text-text-ep"
|
|
72
|
+
>{{ middleEllipsis(eponymeLabel, 36) }}</span>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div class="ep:hidden ep:min-w-0 ep:flex-1 ep:items-center ep:sm:flex">
|
|
76
|
+
<template
|
|
77
|
+
v-for="breadcrumb in visibleBreadcrumbs"
|
|
78
|
+
:key="breadcrumb.path"
|
|
79
|
+
>
|
|
80
|
+
<NuxtLink
|
|
81
|
+
v-if="breadcrumb.kind === 'link'"
|
|
82
|
+
:to="`${basePath}/${breadcrumb.path}`"
|
|
83
|
+
:title="breadcrumb.label"
|
|
84
|
+
class="ep:max-w-40 ep:shrink-0 ep:overflow-hidden ep:text-ellipsis ep:whitespace-nowrap ep:rounded-md ep:px-2 ep:py-1.5 ep:font-medium ep:text-muted-ep ep:no-underline ep:transition ep:hover:bg-selected-ep ep:hover:text-white"
|
|
85
|
+
>
|
|
86
|
+
{{ middleEllipsis(breadcrumb.label) }}
|
|
87
|
+
</NuxtLink>
|
|
88
|
+
<div
|
|
89
|
+
v-else
|
|
90
|
+
class="ep:group ep:relative ep:shrink-0"
|
|
91
|
+
>
|
|
92
|
+
<button
|
|
93
|
+
type="button"
|
|
94
|
+
class="ep:cursor-default ep:rounded-md ep:border-0 ep:bg-transparent ep:px-2 ep:py-1.5 ep:font-semibold ep:text-muted-ep ep:transition ep:hover:bg-selected-ep ep:hover:text-white ep:focus-visible:bg-selected-ep ep:focus-visible:text-white ep:focus-visible:outline-none ep:focus-visible:ring-2 ep:focus-visible:ring-white/20"
|
|
95
|
+
aria-haspopup="menu"
|
|
96
|
+
:aria-label="`${hiddenBreadcrumbs.length} hidden folders`"
|
|
97
|
+
>
|
|
98
|
+
…
|
|
99
|
+
</button>
|
|
100
|
+
<div class="ep:invisible ep:absolute ep:top-full ep:left-0 ep:z-[60] ep:w-max ep:max-w-72 ep:pt-1 ep:opacity-0 ep:transition ep:group-hover:visible ep:group-hover:opacity-100 ep:group-focus-within:visible ep:group-focus-within:opacity-100">
|
|
101
|
+
<div class="ep:relative ep:overflow-hidden ep:rounded-xl ep:border ep:border-border-ep ep:bg-theme-ep ep:p-2">
|
|
102
|
+
<div
|
|
103
|
+
:key="folder"
|
|
104
|
+
role="menu"
|
|
105
|
+
class="eponyme-breadcrumb-menu ep:grid ep:max-h-[212px] ep:gap-1 ep:overflow-y-auto ep:overscroll-contain ep:pr-1"
|
|
106
|
+
@scroll.passive="updateBreadcrumbMenuScroll"
|
|
107
|
+
>
|
|
108
|
+
<NuxtLink
|
|
109
|
+
v-for="hiddenBreadcrumb in hiddenBreadcrumbs"
|
|
110
|
+
:key="hiddenBreadcrumb.path"
|
|
111
|
+
:to="`${basePath}/${hiddenBreadcrumb.path}`"
|
|
112
|
+
:title="hiddenBreadcrumb.label"
|
|
113
|
+
role="menuitem"
|
|
114
|
+
class="ep:block ep:max-w-64 ep:overflow-hidden ep:text-ellipsis ep:whitespace-nowrap ep:rounded-lg ep:px-3 ep:py-2 ep:font-medium ep:text-text-ep ep:no-underline ep:transition ep:hover:bg-selected-ep ep:hover:text-white"
|
|
115
|
+
>
|
|
116
|
+
{{ hiddenBreadcrumb.label }}
|
|
117
|
+
</NuxtLink>
|
|
118
|
+
</div>
|
|
119
|
+
<Transition name="breadcrumb-scroll-hint">
|
|
120
|
+
<div
|
|
121
|
+
v-if="hiddenBreadcrumbs.length > 6 && !breadcrumbMenuAtBottom"
|
|
122
|
+
class="eponyme-breadcrumb-scroll-hint ep:pointer-events-none ep:absolute ep:right-2 ep:bottom-2 ep:left-2 ep:flex ep:h-14 ep:items-end ep:justify-center ep:pb-1 ep:text-center ep:text-[10px] ep:font-semibold ep:tracking-widest ep:text-muted-ep ep:uppercase"
|
|
123
|
+
aria-hidden="true"
|
|
124
|
+
>
|
|
125
|
+
Scroll ↓
|
|
126
|
+
</div>
|
|
127
|
+
</Transition>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
<span
|
|
132
|
+
class="ep:mx-1 ep:shrink-0 ep:text-muted-ep"
|
|
133
|
+
aria-hidden="true"
|
|
134
|
+
>/</span>
|
|
135
|
+
</template>
|
|
136
|
+
<span
|
|
137
|
+
:title="eponymeLabel"
|
|
138
|
+
class="ep:min-w-0 ep:overflow-hidden ep:text-ellipsis ep:whitespace-nowrap ep:px-2 ep:font-medium ep:text-text-ep"
|
|
139
|
+
>{{ middleEllipsis(eponymeLabel, 36) }}</span>
|
|
140
|
+
</div>
|
|
141
|
+
</nav>
|
|
142
|
+
</template>
|
|
143
|
+
|
|
144
|
+
<style scoped>
|
|
145
|
+
.eponyme-breadcrumb-menu{scrollbar-color:hsla(0,0%,100%,.35) transparent;scrollbar-width:thin}.eponyme-breadcrumb-menu::-webkit-scrollbar{width:6px}.eponyme-breadcrumb-menu::-webkit-scrollbar-thumb{background:hsla(0,0%,100%,.35);border-radius:999px}.eponyme-breadcrumb-scroll-hint{background:linear-gradient(180deg,transparent,var(--ep-color-theme-ep,#1c1c1c) 65%)}.breadcrumb-scroll-hint-enter-active,.breadcrumb-scroll-hint-leave-active{transition:opacity .16s ease,transform .16s ease}.breadcrumb-scroll-hint-enter-from,.breadcrumb-scroll-hint-leave-to{opacity:0;transform:translateY(4px)}@media (prefers-reduced-motion:reduce){.breadcrumb-scroll-hint-enter-active,.breadcrumb-scroll-hint-leave-active{transition:none}}
|
|
146
|
+
</style>
|