@karibsen/eponyme 0.8.0 → 0.8.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/eponyme.d.mts +2 -1
- package/dist/eponyme.mjs +1 -0
- package/dist/module.d.mts +2 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +10 -2
- package/dist/runtime/components/fields/FieldRenderer.vue +5 -0
- package/dist/runtime/components/fields/MediaPlayerField.d.vue.ts +19 -0
- package/dist/runtime/components/fields/MediaPlayerField.vue +70 -0
- package/dist/runtime/components/fields/MediaPlayerField.vue.d.ts +19 -0
- package/dist/runtime/components/ui/EPMediaPreview.d.vue.ts +7 -0
- package/dist/runtime/components/ui/EPMediaPreview.vue +34 -0
- package/dist/runtime/components/ui/EPMediaPreview.vue.d.ts +7 -0
- package/dist/runtime/fields/index.d.ts +2 -0
- package/dist/runtime/fields/index.js +2 -1
- package/dist/runtime/fields/media-player.d.ts +2 -0
- package/dist/runtime/fields/media-player.js +3 -0
- package/dist/runtime/types/eponyme.d.ts +2 -2
- package/dist/runtime/types/field.d.ts +30 -3
- package/dist/runtime/utils/get-field-default-value.js +2 -0
- package/dist/runtime/utils/media-player.d.ts +34 -0
- package/dist/runtime/utils/media-player.js +107 -0
- package/dist/runtime/utils/normalize-eponyme-values.js +2 -0
- package/dist/runtime/utils/validate-eponyme-data.js +28 -0
- package/dist/types.d.mts +3 -1
- package/package.json +1 -1
package/dist/eponyme.d.mts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { EponymeConfig, EponymeSchema, EponymeCollectionOptions, CollectionSlugField, EponymeCollectionDefinition, EponymeFormSchema, EponymeFormOptions, EponymeFormDefinition } from '../dist/runtime/types/index.js';
|
|
2
|
-
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, EponymeVariableDefinition, EponymeVariableSummary, EponymeVariableValue, EponymeVariables, 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';
|
|
2
|
+
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, EponymeVariableDefinition, EponymeVariableSummary, EponymeVariableValue, EponymeVariables, FieldDefinition, FieldValidationResult, FieldValidator, FieldVisibilityCondition, ImageFieldDefinition, ImageFieldOptions, MediaPlayerFieldDefinition, MediaPlayerFieldOptions, MediaPlayerProvider, MediaPlayerValue, 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';
|
|
3
3
|
import { EponymeVariables } from '../dist/runtime/types/variables.js';
|
|
4
4
|
export { field, today } from '../dist/runtime/fields/index.js';
|
|
5
5
|
export { EPONYME_ROLES, canEditEponyme, isEponymeRole } from '../dist/runtime/types/auth.js';
|
|
6
|
+
export { eponymeMediaEmbedUrl, eponymeMediaThumbnailUrl, parseEponymeMediaUrl } from '../dist/runtime/utils/media-player.js';
|
|
6
7
|
|
|
7
8
|
declare function defineEponymeConfig<const T extends EponymeConfig>(config: T): T;
|
|
8
9
|
|
package/dist/eponyme.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { field, today } from '../dist/runtime/fields/index.js';
|
|
2
2
|
export { EPONYME_ROLES, canEditEponyme, isEponymeRole } from '../dist/runtime/types/auth.js';
|
|
3
|
+
export { eponymeMediaEmbedUrl, eponymeMediaThumbnailUrl, parseEponymeMediaUrl } from '../dist/runtime/utils/media-player.js';
|
|
3
4
|
|
|
4
5
|
function defineEponymeConfig(config) {
|
|
5
6
|
return config;
|
package/dist/module.d.mts
CHANGED
|
@@ -2,7 +2,8 @@ import * as _nuxt_schema from '@nuxt/schema';
|
|
|
2
2
|
export { collection, defineEponymeConfig, defineEponymeVariables, form } from './eponyme.mjs';
|
|
3
3
|
export { field, today } from '../dist/runtime/fields/index.js';
|
|
4
4
|
export { EPONYME_ROLES, canEditEponyme, isEponymeRole } from '../dist/runtime/types/auth.js';
|
|
5
|
-
export {
|
|
5
|
+
export { eponymeMediaEmbedUrl, eponymeMediaThumbnailUrl, parseEponymeMediaUrl } from '../dist/runtime/utils/media-player.js';
|
|
6
|
+
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, EponymeVariableDefinition, EponymeVariableSummary, EponymeVariableValue, EponymeVariables, FieldDefinition, FieldValidationResult, FieldValidator, FieldVisibilityCondition, ImageFieldDefinition, ImageFieldOptions, MediaPlayerFieldDefinition, MediaPlayerFieldOptions, MediaPlayerProvider, MediaPlayerValue, 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
|
import '../dist/runtime/types/variables.js';
|
|
7
8
|
|
|
8
9
|
/**
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -68,7 +68,11 @@ const module$1 = defineNuxtModule({
|
|
|
68
68
|
"mingcute:back-2-line",
|
|
69
69
|
"mingcute:forward-2-line",
|
|
70
70
|
"mingcute:download-2-line",
|
|
71
|
-
"mingcute:upload-2-line"
|
|
71
|
+
"mingcute:upload-2-line",
|
|
72
|
+
"mingcute:youtube-line",
|
|
73
|
+
"mingcute:movie-line",
|
|
74
|
+
"mingcute:film-line",
|
|
75
|
+
"mingcute:alert-line"
|
|
72
76
|
]
|
|
73
77
|
}
|
|
74
78
|
}
|
|
@@ -123,7 +127,11 @@ Note that '~/' points at ${nuxt.options.srcDir}, so a server module needs '~~/'.
|
|
|
123
127
|
{ name: "form", from: resolver.resolve("./config/form") },
|
|
124
128
|
{ name: "defineEponymeVariables", from: resolver.resolve("./config/variables") },
|
|
125
129
|
{ name: "field", from: resolver.resolve("./runtime/fields") },
|
|
126
|
-
{ name: "today", from: resolver.resolve("./runtime/fields") }
|
|
130
|
+
{ name: "today", from: resolver.resolve("./runtime/fields") },
|
|
131
|
+
// What a page needs to render a `field.mediaPlayer()` value: the embed address is
|
|
132
|
+
// derived from the stored URL rather than stored next to it.
|
|
133
|
+
{ name: "eponymeMediaEmbedUrl", from: resolver.resolve("./runtime/utils/media-player") },
|
|
134
|
+
{ name: "eponymeMediaThumbnailUrl", from: resolver.resolve("./runtime/utils/media-player") }
|
|
127
135
|
]);
|
|
128
136
|
addImportsDir(resolver.resolve("./runtime/composables"));
|
|
129
137
|
addServerImports([
|
|
@@ -4,6 +4,7 @@ import BooleanField from "./BooleanField.vue";
|
|
|
4
4
|
import CheckboxGroupField from "./CheckboxGroupField.vue";
|
|
5
5
|
import ColorField from "./ColorField.vue";
|
|
6
6
|
import DateField from "./DateField.vue";
|
|
7
|
+
import MediaPlayerField from "./MediaPlayerField.vue";
|
|
7
8
|
import NumberField from "./NumberField.vue";
|
|
8
9
|
import PhoneField from "./PhoneField.vue";
|
|
9
10
|
import RadioField from "./RadioField.vue";
|
|
@@ -61,6 +62,8 @@ const component = computed(() => {
|
|
|
61
62
|
return ColorField;
|
|
62
63
|
case "url":
|
|
63
64
|
return UrlField;
|
|
65
|
+
case "mediaPlayer":
|
|
66
|
+
return MediaPlayerField;
|
|
64
67
|
case "phone":
|
|
65
68
|
return PhoneField;
|
|
66
69
|
case "tags":
|
|
@@ -109,6 +112,8 @@ const specificProps = computed(() => {
|
|
|
109
112
|
return { presets: field.options.presets };
|
|
110
113
|
case "url":
|
|
111
114
|
return { placeholder: field.options.placeholder };
|
|
115
|
+
case "mediaPlayer":
|
|
116
|
+
return { placeholder: field.options.placeholder, providers: field.options.providers };
|
|
112
117
|
case "phone":
|
|
113
118
|
return {
|
|
114
119
|
placeholder: field.options.placeholder,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { MediaPlayerProvider, MediaPlayerValue } from '../../types/index.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
id: string;
|
|
4
|
+
modelValue?: unknown;
|
|
5
|
+
label: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
required?: boolean;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
providers?: readonly MediaPlayerProvider[];
|
|
10
|
+
errors?: string[];
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
|
+
"update:modelValue": (value: MediaPlayerValue) => any;
|
|
15
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
16
|
+
"onUpdate:modelValue"?: ((value: MediaPlayerValue) => any) | undefined;
|
|
17
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
18
|
+
declare const _default: typeof __VLS_export;
|
|
19
|
+
export default _default;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import EPFormField from "../ui/EPFormField.vue";
|
|
4
|
+
import EPInputText from "../ui/EPInputText.vue";
|
|
5
|
+
import EPMediaPreview from "../ui/EPMediaPreview.vue";
|
|
6
|
+
import { mediaPlayerProviders, parseEponymeMediaUrl, toMediaPlayerValue } from "../../utils/media-player";
|
|
7
|
+
const props = defineProps({
|
|
8
|
+
id: { type: String, required: true },
|
|
9
|
+
modelValue: { type: null, required: false },
|
|
10
|
+
label: { type: String, required: true },
|
|
11
|
+
description: { type: String, required: false },
|
|
12
|
+
required: { type: Boolean, required: false },
|
|
13
|
+
placeholder: { type: String, required: false },
|
|
14
|
+
providers: { type: Array, required: false },
|
|
15
|
+
errors: { type: Array, required: false, default: () => [] },
|
|
16
|
+
disabled: { type: Boolean, required: false }
|
|
17
|
+
});
|
|
18
|
+
const emit = defineEmits(["update:modelValue"]);
|
|
19
|
+
const PROVIDER_LABELS = {
|
|
20
|
+
youtube: { label: "YouTube", icon: "mingcute:youtube-line" },
|
|
21
|
+
// mingcute has no Vimeo mark, so the generic video icon stands in for it.
|
|
22
|
+
vimeo: { label: "Vimeo", icon: "mingcute:movie-line" },
|
|
23
|
+
url: { label: "Direct video", icon: "mingcute:film-line" }
|
|
24
|
+
};
|
|
25
|
+
const value = computed(() => toMediaPlayerValue(props.modelValue));
|
|
26
|
+
const providers = computed(() => mediaPlayerProviders({ providers: props.providers }));
|
|
27
|
+
const detected = computed(() => value.value.provider ? PROVIDER_LABELS[value.value.provider] : void 0);
|
|
28
|
+
const placeholder = computed(() => props.placeholder ?? `${providers.value.map((provider) => PROVIDER_LABELS[provider]?.label).join(", ")} address`);
|
|
29
|
+
function update(url) {
|
|
30
|
+
emit("update:modelValue", parseEponymeMediaUrl(String(url), { providers: props.providers }));
|
|
31
|
+
}
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<template>
|
|
35
|
+
<EPFormField
|
|
36
|
+
:id="id"
|
|
37
|
+
:label="label"
|
|
38
|
+
:description="description"
|
|
39
|
+
:required="required"
|
|
40
|
+
:errors="errors"
|
|
41
|
+
>
|
|
42
|
+
<EPInputText
|
|
43
|
+
:id="id"
|
|
44
|
+
:model-value="value.url"
|
|
45
|
+
type="url"
|
|
46
|
+
:placeholder="placeholder"
|
|
47
|
+
:required="required"
|
|
48
|
+
:invalid="Boolean(errors.length)"
|
|
49
|
+
:disabled="disabled"
|
|
50
|
+
@update:model-value="update"
|
|
51
|
+
/>
|
|
52
|
+
<p
|
|
53
|
+
v-if="value.url"
|
|
54
|
+
class="ep:mt-1.5 ep:mb-0 ep:flex ep:items-center ep:gap-1.5 ep:text-[11px] ep:text-muted-ep"
|
|
55
|
+
>
|
|
56
|
+
<Icon
|
|
57
|
+
:name="detected ? detected.icon : 'mingcute:alert-line'"
|
|
58
|
+
size="14"
|
|
59
|
+
aria-hidden="true"
|
|
60
|
+
/>
|
|
61
|
+
{{ detected ? detected.label : "Unrecognized address" }}
|
|
62
|
+
</p>
|
|
63
|
+
<EPMediaPreview
|
|
64
|
+
v-if="detected"
|
|
65
|
+
:model-value="value"
|
|
66
|
+
:label="label"
|
|
67
|
+
class="ep:mt-3"
|
|
68
|
+
/>
|
|
69
|
+
</EPFormField>
|
|
70
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { MediaPlayerProvider, MediaPlayerValue } from '../../types/index.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
id: string;
|
|
4
|
+
modelValue?: unknown;
|
|
5
|
+
label: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
required?: boolean;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
providers?: readonly MediaPlayerProvider[];
|
|
10
|
+
errors?: string[];
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
|
+
"update:modelValue": (value: MediaPlayerValue) => any;
|
|
15
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
16
|
+
"onUpdate:modelValue"?: ((value: MediaPlayerValue) => any) | undefined;
|
|
17
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
18
|
+
declare const _default: typeof __VLS_export;
|
|
19
|
+
export default _default;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
modelValue?: unknown;
|
|
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,34 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { eponymeMediaEmbedUrl, toMediaPlayerValue } from "../../utils/media-player";
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
modelValue: { type: null, required: false },
|
|
6
|
+
label: { type: String, required: false }
|
|
7
|
+
});
|
|
8
|
+
const media = computed(() => toMediaPlayerValue(props.modelValue));
|
|
9
|
+
const embedUrl = computed(() => eponymeMediaEmbedUrl(media.value));
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<div class="ep:aspect-video ep:w-full ep:overflow-hidden ep:rounded-xl ep:border ep:border-border-ep ep:bg-black">
|
|
14
|
+
<video
|
|
15
|
+
v-if="media.provider === 'url'"
|
|
16
|
+
:key="embedUrl"
|
|
17
|
+
:src="embedUrl"
|
|
18
|
+
controls
|
|
19
|
+
preload="metadata"
|
|
20
|
+
class="ep:h-full ep:w-full ep:object-contain"
|
|
21
|
+
/>
|
|
22
|
+
<iframe
|
|
23
|
+
v-else
|
|
24
|
+
:key="embedUrl"
|
|
25
|
+
:src="embedUrl"
|
|
26
|
+
:title="label || 'Video preview'"
|
|
27
|
+
loading="lazy"
|
|
28
|
+
referrerpolicy="strict-origin-when-cross-origin"
|
|
29
|
+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; fullscreen"
|
|
30
|
+
allowfullscreen
|
|
31
|
+
class="ep:h-full ep:w-full ep:border-0"
|
|
32
|
+
/>
|
|
33
|
+
</div>
|
|
34
|
+
</template>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
modelValue?: unknown;
|
|
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;
|
|
@@ -5,6 +5,7 @@ import { color } from './color.js';
|
|
|
5
5
|
import { date, today } from './date.js';
|
|
6
6
|
import { email } from './email.js';
|
|
7
7
|
import { image } from './image.js';
|
|
8
|
+
import { mediaPlayer } from './media-player.js';
|
|
8
9
|
import { number } from './number.js';
|
|
9
10
|
import { phone } from './phone.js';
|
|
10
11
|
import { radio } from './radio.js';
|
|
@@ -23,6 +24,7 @@ export declare const field: {
|
|
|
23
24
|
email: typeof email;
|
|
24
25
|
phone: typeof phone;
|
|
25
26
|
url: typeof url;
|
|
27
|
+
mediaPlayer: typeof mediaPlayer;
|
|
26
28
|
textarea: typeof textarea;
|
|
27
29
|
richText: typeof richText;
|
|
28
30
|
number: typeof number;
|
|
@@ -5,6 +5,7 @@ import { color } from "./color.js";
|
|
|
5
5
|
import { date, today } from "./date.js";
|
|
6
6
|
import { email } from "./email.js";
|
|
7
7
|
import { image } from "./image.js";
|
|
8
|
+
import { mediaPlayer } from "./media-player.js";
|
|
8
9
|
import { number } from "./number.js";
|
|
9
10
|
import { phone } from "./phone.js";
|
|
10
11
|
import { radio } from "./radio.js";
|
|
@@ -17,5 +18,5 @@ import { tags } from "./tags.js";
|
|
|
17
18
|
import { tab } from "./tab.js";
|
|
18
19
|
import { textarea } from "./textarea.js";
|
|
19
20
|
import { url } from "./url.js";
|
|
20
|
-
export const field = { string, slug, email, phone, url, textarea, richText, number, boolean, image, select, radio, checkboxGroup, tags, date, color, array, section, tab };
|
|
21
|
+
export const field = { string, slug, email, phone, url, mediaPlayer, textarea, richText, number, boolean, image, select, radio, checkboxGroup, tags, date, color, array, section, tab };
|
|
21
22
|
export { today };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ArrayFieldDefinition, ArrayItemValue, BooleanFieldDefinition, CheckboxGroupFieldDefinition, EmailFieldDefinition, FieldDefinition, NumberFieldDefinition, PhoneFieldDefinition, RadioFieldDefinition, SectionFieldDefinition, SectionValue, SelectFieldDefinition, SlugFieldDefinition, StringFieldDefinition, TabFieldDefinition, TagsFieldDefinition, TagsValueOf, TabsValue, TextareaFieldDefinition, UrlFieldDefinition, UrlValue } from './field.js';
|
|
1
|
+
import type { ArrayFieldDefinition, ArrayItemValue, BooleanFieldDefinition, CheckboxGroupFieldDefinition, EmailFieldDefinition, FieldDefinition, MediaPlayerFieldDefinition, MediaPlayerValue, NumberFieldDefinition, PhoneFieldDefinition, RadioFieldDefinition, SectionFieldDefinition, SectionValue, SelectFieldDefinition, SlugFieldDefinition, StringFieldDefinition, TabFieldDefinition, TagsFieldDefinition, TagsValueOf, TabsValue, TextareaFieldDefinition, UrlFieldDefinition, UrlValue } from './field.js';
|
|
2
2
|
export type EponymeSchema = Record<string, FieldDefinition>;
|
|
3
3
|
export interface EponymeCollectionDefinitionBase {
|
|
4
4
|
__eponymeCollection: true;
|
|
@@ -65,7 +65,7 @@ export type FieldValue<T extends FieldDefinition> = T extends {
|
|
|
65
65
|
type: 'number';
|
|
66
66
|
} ? number : T extends {
|
|
67
67
|
type: 'boolean';
|
|
68
|
-
} ? boolean : T extends UrlFieldDefinition ? UrlValue : T extends ArrayFieldDefinition<infer Item> ? Array<ArrayItemValue<Item>> : T extends CheckboxGroupFieldDefinition<infer Value> ? Value[] : T extends TagsFieldDefinition<infer Options> ? Array<TagsValueOf<Options>> : T extends SectionFieldDefinition<infer Section> ? SectionValue<Section> : T extends TabFieldDefinition<infer Tabs> ? TabsValue<Tabs> : string;
|
|
68
|
+
} ? boolean : T extends UrlFieldDefinition ? UrlValue : T extends MediaPlayerFieldDefinition ? MediaPlayerValue : T extends ArrayFieldDefinition<infer Item> ? Array<ArrayItemValue<Item>> : T extends CheckboxGroupFieldDefinition<infer Value> ? Value[] : T extends TagsFieldDefinition<infer Options> ? Array<TagsValueOf<Options>> : T extends SectionFieldDefinition<infer Section> ? SectionValue<Section> : T extends TabFieldDefinition<infer Tabs> ? TabsValue<Tabs> : string;
|
|
69
69
|
export type EponymeData<T extends EponymeSchema> = {
|
|
70
70
|
[K in keyof T]: T[K] extends FieldDefinition ? FieldValue<T[K]> : never;
|
|
71
71
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CountryCode } from 'libphonenumber-js/min';
|
|
2
|
-
export type EponymeFieldType = 'string' | 'slug' | 'email' | 'phone' | 'url' | 'textarea' | 'richText' | 'number' | 'boolean' | 'image' | 'select' | 'radio' | 'checkboxGroup' | 'tags' | 'date' | 'color' | 'array' | 'section' | 'tabs';
|
|
2
|
+
export type EponymeFieldType = 'string' | 'slug' | 'email' | 'phone' | 'url' | 'mediaPlayer' | 'textarea' | 'richText' | 'number' | 'boolean' | 'image' | 'select' | 'radio' | 'checkboxGroup' | 'tags' | 'date' | 'color' | 'array' | 'section' | 'tabs';
|
|
3
3
|
export type FieldVisibilityCondition = {
|
|
4
4
|
field: string;
|
|
5
5
|
equals: unknown;
|
|
@@ -124,6 +124,33 @@ export interface UrlFieldDefinition {
|
|
|
124
124
|
type: 'url';
|
|
125
125
|
options: UrlFieldOptions;
|
|
126
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Where a video comes from. `youtube` and `vimeo` are embedded in an iframe; `url` is a file
|
|
129
|
+
* the browser plays directly in a `<video>` element.
|
|
130
|
+
*/
|
|
131
|
+
export type MediaPlayerProvider = 'youtube' | 'vimeo' | 'url';
|
|
132
|
+
export interface MediaPlayerValue {
|
|
133
|
+
/** Detected from `url`. Empty while nothing has been entered, or when nothing recognised it. */
|
|
134
|
+
provider: MediaPlayerProvider | '';
|
|
135
|
+
/** Address as it was entered. */
|
|
136
|
+
url: string;
|
|
137
|
+
/** Video id at its provider. Empty for `url`, which has none. */
|
|
138
|
+
id: string;
|
|
139
|
+
}
|
|
140
|
+
export interface MediaPlayerFieldOptions extends DefaultFieldOptions<MediaPlayerValue> {
|
|
141
|
+
placeholder?: string;
|
|
142
|
+
/**
|
|
143
|
+
* Sources this field accepts. A video from any other one is refused rather than stored as
|
|
144
|
+
* an address nothing can play.
|
|
145
|
+
*
|
|
146
|
+
* @default ['youtube', 'vimeo', 'url']
|
|
147
|
+
*/
|
|
148
|
+
providers?: readonly MediaPlayerProvider[];
|
|
149
|
+
}
|
|
150
|
+
export interface MediaPlayerFieldDefinition {
|
|
151
|
+
type: 'mediaPlayer';
|
|
152
|
+
options: MediaPlayerFieldOptions;
|
|
153
|
+
}
|
|
127
154
|
export interface NumberFieldOptions extends DefaultFieldOptions<number> {
|
|
128
155
|
min?: number;
|
|
129
156
|
max?: number;
|
|
@@ -233,10 +260,10 @@ export interface ColorFieldDefinition {
|
|
|
233
260
|
type: 'color';
|
|
234
261
|
options: ColorFieldOptions;
|
|
235
262
|
}
|
|
236
|
-
export type ArrayItemFieldDefinition = StringFieldDefinition | SlugFieldDefinition | EmailFieldDefinition | PhoneFieldDefinition | UrlFieldDefinition | TextareaFieldDefinition | RichTextFieldDefinition | NumberFieldDefinition | BooleanFieldDefinition | ImageFieldDefinition | SelectFieldDefinition | RadioFieldDefinition | CheckboxGroupFieldDefinition | TagsFieldDefinition | DateFieldDefinition | ColorFieldDefinition;
|
|
263
|
+
export type ArrayItemFieldDefinition = StringFieldDefinition | SlugFieldDefinition | EmailFieldDefinition | PhoneFieldDefinition | UrlFieldDefinition | MediaPlayerFieldDefinition | TextareaFieldDefinition | RichTextFieldDefinition | NumberFieldDefinition | BooleanFieldDefinition | ImageFieldDefinition | SelectFieldDefinition | RadioFieldDefinition | CheckboxGroupFieldDefinition | TagsFieldDefinition | DateFieldDefinition | ColorFieldDefinition;
|
|
237
264
|
export type ArrayItemSchema = Record<string, ArrayItemFieldDefinition>;
|
|
238
265
|
export type ArrayItemDefinition = ArrayItemFieldDefinition | ArrayItemSchema;
|
|
239
|
-
export type ArrayItemValue<T extends ArrayItemDefinition> = T extends NumberFieldDefinition ? number : T extends BooleanFieldDefinition ? boolean : T extends UrlFieldDefinition ? UrlValue : T extends CheckboxGroupFieldDefinition<infer Value> ? Value[] : T extends ArrayItemFieldDefinition ? string : T extends ArrayItemSchema ? {
|
|
266
|
+
export type ArrayItemValue<T extends ArrayItemDefinition> = T extends NumberFieldDefinition ? number : T extends BooleanFieldDefinition ? boolean : T extends UrlFieldDefinition ? UrlValue : T extends MediaPlayerFieldDefinition ? MediaPlayerValue : T extends CheckboxGroupFieldDefinition<infer Value> ? Value[] : T extends ArrayItemFieldDefinition ? string : T extends ArrayItemSchema ? {
|
|
240
267
|
[K in keyof T]: ArrayItemValue<T[K]>;
|
|
241
268
|
} : never;
|
|
242
269
|
export interface ArrayFieldOptions<T extends ArrayItemDefinition> extends DefaultFieldOptions<Array<ArrayItemValue<T>>> {
|
|
@@ -34,6 +34,8 @@ export function getFieldDefaultValue(definition) {
|
|
|
34
34
|
return 0;
|
|
35
35
|
case "url":
|
|
36
36
|
return { href: "", type: "external", openInNewTab: false, download: false };
|
|
37
|
+
case "mediaPlayer":
|
|
38
|
+
return { provider: "", url: "", id: "" };
|
|
37
39
|
case "boolean":
|
|
38
40
|
return false;
|
|
39
41
|
default:
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { MediaPlayerFieldOptions, MediaPlayerProvider, MediaPlayerValue } from '../types/field.js';
|
|
2
|
+
export declare const MEDIA_PLAYER_PROVIDERS: readonly MediaPlayerProvider[];
|
|
3
|
+
export declare const EMPTY_MEDIA_PLAYER_VALUE: MediaPlayerValue;
|
|
4
|
+
/** Providers a field accepts, in detection order, with the default applied. */
|
|
5
|
+
export declare function mediaPlayerProviders(options?: Pick<MediaPlayerFieldOptions, 'providers'>): MediaPlayerProvider[];
|
|
6
|
+
/**
|
|
7
|
+
* Reads a pasted address into the value the field stores.
|
|
8
|
+
*
|
|
9
|
+
* The provider is detected rather than picked in the interface: an editor pastes the address
|
|
10
|
+
* they have, and a provider chosen by hand is one more thing that can contradict the URL.
|
|
11
|
+
* `url` is the fallback for a file served directly — anything the browser can play in a
|
|
12
|
+
* `<video>` — so it only answers once the hosted providers have declined.
|
|
13
|
+
*
|
|
14
|
+
* `provider` is empty when nothing recognised it, which is what validation reports. The parse
|
|
15
|
+
* never throws, so an address still being typed simply reads as unrecognised.
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseEponymeMediaUrl(url: unknown, options?: Pick<MediaPlayerFieldOptions, 'providers'>): MediaPlayerValue;
|
|
18
|
+
/**
|
|
19
|
+
* Rereads a stored value, so `provider` and `id` always describe the URL they sit next to.
|
|
20
|
+
* The API accepts any JSON, so a client is never what decides which provider a value claims.
|
|
21
|
+
*/
|
|
22
|
+
export declare function normalizeEponymeMediaPlayer(value: unknown, options?: Pick<MediaPlayerFieldOptions, 'providers'>): unknown;
|
|
23
|
+
/**
|
|
24
|
+
* Address to put in an `iframe` for a hosted provider, or the file itself for `url` — which
|
|
25
|
+
* belongs in a `<video>` instead. Empty when the value carries nothing playable.
|
|
26
|
+
*
|
|
27
|
+
* Derived on read rather than stored: an embed address is the provider's contract, not the
|
|
28
|
+
* editor's input, and a stored copy would keep serving yesterday's parameters.
|
|
29
|
+
*/
|
|
30
|
+
export declare function eponymeMediaEmbedUrl(value: unknown): string;
|
|
31
|
+
/** Poster image for a hosted provider. Vimeo has no address-only thumbnail, so it has none. */
|
|
32
|
+
export declare function eponymeMediaThumbnailUrl(value: unknown): string;
|
|
33
|
+
/** Reads unknown input into the value shape, so a partial or absent value still renders. */
|
|
34
|
+
export declare function toMediaPlayerValue(value: unknown): MediaPlayerValue;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
export const MEDIA_PLAYER_PROVIDERS = ["youtube", "vimeo", "url"];
|
|
2
|
+
const YOUTUBE_HOSTS = ["youtube.com", "www.youtube.com", "m.youtube.com", "music.youtube.com", "youtube-nocookie.com", "www.youtube-nocookie.com"];
|
|
3
|
+
const YOUTUBE_SHORT_HOSTS = ["youtu.be", "www.youtu.be"];
|
|
4
|
+
const VIMEO_HOSTS = ["vimeo.com", "www.vimeo.com", "player.vimeo.com"];
|
|
5
|
+
const YOUTUBE_ID = /^[\w-]{6,20}$/;
|
|
6
|
+
const VIMEO_ID = /^\d+$/;
|
|
7
|
+
export const EMPTY_MEDIA_PLAYER_VALUE = { provider: "", url: "", id: "" };
|
|
8
|
+
export function mediaPlayerProviders(options = {}) {
|
|
9
|
+
const providers = options.providers?.filter((provider) => MEDIA_PLAYER_PROVIDERS.includes(provider));
|
|
10
|
+
return providers?.length ? [...new Set(providers)] : [...MEDIA_PLAYER_PROVIDERS];
|
|
11
|
+
}
|
|
12
|
+
export function parseEponymeMediaUrl(url, options = {}) {
|
|
13
|
+
const raw = typeof url === "string" ? url.trim() : "";
|
|
14
|
+
if (!raw) return { ...EMPTY_MEDIA_PLAYER_VALUE };
|
|
15
|
+
const allowed = mediaPlayerProviders(options);
|
|
16
|
+
const parsed = parseUrl(raw);
|
|
17
|
+
if (!parsed) return { provider: "", url: raw, id: "" };
|
|
18
|
+
const host = parsed.hostname.toLowerCase();
|
|
19
|
+
if (allowed.includes("youtube")) {
|
|
20
|
+
const id = youtubeId(parsed, host);
|
|
21
|
+
if (id) return { provider: "youtube", url: raw, id };
|
|
22
|
+
}
|
|
23
|
+
if (allowed.includes("vimeo")) {
|
|
24
|
+
const id = vimeoId(parsed, host);
|
|
25
|
+
if (id) return { provider: "vimeo", url: raw, id };
|
|
26
|
+
}
|
|
27
|
+
if (isHostedHost(host)) return { provider: "", url: raw, id: "" };
|
|
28
|
+
if (allowed.includes("url") && (parsed.protocol === "http:" || parsed.protocol === "https:"))
|
|
29
|
+
return { provider: "url", url: raw, id: "" };
|
|
30
|
+
return { provider: "", url: raw, id: "" };
|
|
31
|
+
}
|
|
32
|
+
export function normalizeEponymeMediaPlayer(value, options = {}) {
|
|
33
|
+
if (typeof value === "string") return parseEponymeMediaUrl(value, options);
|
|
34
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return value;
|
|
35
|
+
const url = value.url;
|
|
36
|
+
if (typeof url !== "string") return value;
|
|
37
|
+
return parseEponymeMediaUrl(url, options);
|
|
38
|
+
}
|
|
39
|
+
export function eponymeMediaEmbedUrl(value) {
|
|
40
|
+
const media = toMediaPlayerValue(value);
|
|
41
|
+
if (!media.url) return "";
|
|
42
|
+
if (media.provider === "url") return media.url;
|
|
43
|
+
if (!media.id) return "";
|
|
44
|
+
if (media.provider === "youtube")
|
|
45
|
+
return `https://www.youtube-nocookie.com/embed/${encodeURIComponent(media.id)}`;
|
|
46
|
+
if (media.provider === "vimeo") {
|
|
47
|
+
const hash = vimeoPrivacyHash(media.url);
|
|
48
|
+
return `https://player.vimeo.com/video/${encodeURIComponent(media.id)}${hash ? `?h=${encodeURIComponent(hash)}` : ""}`;
|
|
49
|
+
}
|
|
50
|
+
return "";
|
|
51
|
+
}
|
|
52
|
+
export function eponymeMediaThumbnailUrl(value) {
|
|
53
|
+
const media = toMediaPlayerValue(value);
|
|
54
|
+
if (media.provider === "youtube" && media.id) return `https://i.ytimg.com/vi/${encodeURIComponent(media.id)}/hqdefault.jpg`;
|
|
55
|
+
return "";
|
|
56
|
+
}
|
|
57
|
+
export function toMediaPlayerValue(value) {
|
|
58
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return { ...EMPTY_MEDIA_PLAYER_VALUE };
|
|
59
|
+
const media = value;
|
|
60
|
+
const provider = typeof media.provider === "string" && MEDIA_PLAYER_PROVIDERS.includes(media.provider) ? media.provider : "";
|
|
61
|
+
return {
|
|
62
|
+
provider,
|
|
63
|
+
url: typeof media.url === "string" ? media.url : "",
|
|
64
|
+
id: typeof media.id === "string" ? media.id : ""
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function parseUrl(raw) {
|
|
68
|
+
try {
|
|
69
|
+
return new URL(raw);
|
|
70
|
+
} catch {
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function isHostedHost(host) {
|
|
75
|
+
return YOUTUBE_HOSTS.includes(host) || YOUTUBE_SHORT_HOSTS.includes(host) || VIMEO_HOSTS.includes(host);
|
|
76
|
+
}
|
|
77
|
+
function youtubeId(url, host) {
|
|
78
|
+
if (YOUTUBE_SHORT_HOSTS.includes(host)) return validId(segments(url)[0], YOUTUBE_ID);
|
|
79
|
+
if (!YOUTUBE_HOSTS.includes(host)) return "";
|
|
80
|
+
const parts = segments(url);
|
|
81
|
+
if (parts[0] === "watch") return validId(url.searchParams.get("v"), YOUTUBE_ID);
|
|
82
|
+
if (parts[0] && ["embed", "shorts", "live", "v"].includes(parts[0])) return validId(parts[1], YOUTUBE_ID);
|
|
83
|
+
return "";
|
|
84
|
+
}
|
|
85
|
+
function vimeoId(url, host) {
|
|
86
|
+
if (!VIMEO_HOSTS.includes(host)) return "";
|
|
87
|
+
const parts = segments(url);
|
|
88
|
+
if (host === "player.vimeo.com") return parts[0] === "video" ? validId(parts[1], VIMEO_ID) : "";
|
|
89
|
+
const numeric = [...parts].reverse().find((part) => VIMEO_ID.test(part));
|
|
90
|
+
return validId(numeric, VIMEO_ID);
|
|
91
|
+
}
|
|
92
|
+
function vimeoPrivacyHash(raw) {
|
|
93
|
+
const url = parseUrl(raw);
|
|
94
|
+
if (!url) return "";
|
|
95
|
+
const query = url.searchParams.get("h");
|
|
96
|
+
if (query) return query;
|
|
97
|
+
const parts = segments(url);
|
|
98
|
+
const index = parts.findIndex((part) => VIMEO_ID.test(part));
|
|
99
|
+
const hash = index >= 0 ? parts[index + 1] : void 0;
|
|
100
|
+
return hash && /^[\da-z]+$/i.test(hash) ? hash : "";
|
|
101
|
+
}
|
|
102
|
+
function segments(url) {
|
|
103
|
+
return url.pathname.split("/").filter(Boolean);
|
|
104
|
+
}
|
|
105
|
+
function validId(candidate, pattern) {
|
|
106
|
+
return candidate && pattern.test(candidate) ? candidate : "";
|
|
107
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isArrayItemFieldDefinition } from "./get-field-default-value.js";
|
|
2
|
+
import { normalizeEponymeMediaPlayer } from "./media-player.js";
|
|
2
3
|
import { toEponymePhoneValue } from "./normalize-phone.js";
|
|
3
4
|
import { normalizeEponymeTags } from "./normalize-tags.js";
|
|
4
5
|
export function normalizeEponymeValues(schema, data) {
|
|
@@ -12,6 +13,7 @@ export function normalizeEponymeValues(schema, data) {
|
|
|
12
13
|
function normalizeField(definition, value) {
|
|
13
14
|
if (definition.type === "phone") return toEponymePhoneValue(value, definition.options);
|
|
14
15
|
if (definition.type === "tags") return Array.isArray(value) ? normalizeEponymeTags(value, definition.options) : value;
|
|
16
|
+
if (definition.type === "mediaPlayer") return normalizeEponymeMediaPlayer(value, definition.options);
|
|
15
17
|
if (definition.type === "section")
|
|
16
18
|
return normalizeNested(definition.options.fields, value);
|
|
17
19
|
if (definition.type === "tabs") {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isArrayItemFieldDefinition } from "./get-field-default-value.js";
|
|
2
2
|
import { isFieldVisible } from "./is-field-visible.js";
|
|
3
|
+
import { MEDIA_PLAYER_PROVIDERS, mediaPlayerProviders, parseEponymeMediaUrl } from "./media-player.js";
|
|
3
4
|
import { normalizeEponymePhone } from "./normalize-phone.js";
|
|
4
5
|
import { normalizeEponymeTags } from "./normalize-tags.js";
|
|
5
6
|
const urlPattern = /^https?:\/\//i;
|
|
@@ -158,6 +159,23 @@ function validateFieldRules(name, definition, value, errors, mode, _context) {
|
|
|
158
159
|
if (link.type === "internal" && !href.startsWith("/") && !href.startsWith("#")) addError(errors, name, "Internal links must start with / or #.");
|
|
159
160
|
return;
|
|
160
161
|
}
|
|
162
|
+
if (definition.type === "mediaPlayer") {
|
|
163
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
164
|
+
addError(errors, name, "Must be a video.");
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const media = value;
|
|
168
|
+
if (typeof media.url !== "string" || media.id !== void 0 && typeof media.id !== "string" || media.provider !== void 0 && media.provider !== "" && !MEDIA_PLAYER_PROVIDERS.includes(media.provider)) {
|
|
169
|
+
addError(errors, name, "Must be a valid video.");
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const address = media.url.trim();
|
|
173
|
+
if (mode === "publish" && options.required && !address) addError(errors, name, "This field is required.");
|
|
174
|
+
if (!address) return;
|
|
175
|
+
const parsed = parseEponymeMediaUrl(address, definition.options);
|
|
176
|
+
if (!parsed.provider) addError(errors, name, mediaPlayerError(definition.options));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
161
179
|
if (typeof value !== "string") {
|
|
162
180
|
addError(errors, name, "Must be a string.");
|
|
163
181
|
return;
|
|
@@ -224,6 +242,16 @@ function isAllowedProtocol(href, protocols) {
|
|
|
224
242
|
const scheme = schemePattern.exec(href)?.[1]?.toLowerCase();
|
|
225
243
|
return Boolean(scheme) && normalizeProtocols(protocols).includes(scheme);
|
|
226
244
|
}
|
|
245
|
+
const MEDIA_PLAYER_LABELS = {
|
|
246
|
+
youtube: "a YouTube link",
|
|
247
|
+
vimeo: "a Vimeo link",
|
|
248
|
+
url: "a direct video URL"
|
|
249
|
+
};
|
|
250
|
+
function mediaPlayerError(options) {
|
|
251
|
+
const accepted = mediaPlayerProviders(options).map((provider) => MEDIA_PLAYER_LABELS[provider]);
|
|
252
|
+
const last = accepted.pop();
|
|
253
|
+
return `Must be ${accepted.length ? `${accepted.join(", ")} or ${last}` : last}.`;
|
|
254
|
+
}
|
|
227
255
|
function getRichTextContent(value) {
|
|
228
256
|
return value.replace(/<br\s*\/?>/gi, " ").replace(/<\/p>|<\/h[1-6]>|<\/li>|<\/blockquote>/gi, " ").replace(/<[^>]+>/g, "").replace(/ | /gi, " ").replace(/&/gi, "&").replace(/</gi, "<").replace(/>/gi, ">").replace(/\s+/g, " ").trim();
|
|
229
257
|
}
|
package/dist/types.d.mts
CHANGED
|
@@ -4,7 +4,9 @@ export { type field, type today } from '../dist/runtime/fields/index.js'
|
|
|
4
4
|
|
|
5
5
|
export { type EPONYME_ROLES, type canEditEponyme, type isEponymeRole } from '../dist/runtime/types/auth.js'
|
|
6
6
|
|
|
7
|
-
export { type
|
|
7
|
+
export { type eponymeMediaEmbedUrl, type eponymeMediaThumbnailUrl, type parseEponymeMediaUrl } from '../dist/runtime/utils/media-player.js'
|
|
8
|
+
|
|
9
|
+
export { type ArrayFieldDefinition, type ArrayFieldOptions, type ArrayItemDefinition, type ArrayItemFieldDefinition, type ArrayItemSchema, type ArrayItemValue, type BooleanFieldDefinition, type BooleanFieldOptions, type CheckboxGroupFieldDefinition, type CheckboxGroupFieldOptions, type CollectionSlugField, type ColorFieldDefinition, type ColorFieldOptions, type DateFieldDefinition, type DateFieldOptions, type EmailFieldDefinition, type EmailFieldOptions, type EponymeAuthSession, type EponymeAuthUser, type EponymeCollectionDataByName, type EponymeCollectionDefinition, type EponymeCollectionDefinitionBase, type EponymeCollectionName, type EponymeCollectionOptions, type EponymeConfig, type EponymeData, type EponymeDataByName, type EponymeFormDataByName, type EponymeFormDefinition, type EponymeFormDefinitionBase, type EponymeFormFieldDefinition, type EponymeFormMode, type EponymeFormName, type EponymeFormOptions, type EponymeFormSchema, type EponymeManagedUserResult, type EponymeName, type EponymeRole, type EponymeSchema, type EponymeVariableDefinition, type EponymeVariableSummary, type EponymeVariableValue, type EponymeVariables, type FieldDefinition, type FieldValidationResult, type FieldValidator, type FieldVisibilityCondition, type ImageFieldDefinition, type ImageFieldOptions, type MediaPlayerFieldDefinition, type MediaPlayerFieldOptions, type MediaPlayerProvider, type MediaPlayerValue, type NumberFieldDefinition, type NumberFieldOptions, type RadioFieldDefinition, type RadioFieldOptions, type RichTextFieldDefinition, type RichTextFieldOptions, type SectionFieldDefinition, type SectionFieldOptions, type SectionItemFieldDefinition, type SectionItemValue, type SectionSchema, type SectionValue, type SelectFieldDefinition, type SelectFieldOptions, type SelectOption, type SlugFieldDefinition, type SlugFieldOptions, type StringFieldDefinition, type StringFieldOptions, type TabDefinition, type TabFieldDefinition, type TabFieldOptions, type TabsSchema, type TabsValue, type TextareaFieldDefinition, type TextareaFieldOptions, type TodayDateDefault, type UrlFieldDefinition, type UrlFieldOptions, type UrlType, type UrlValue } from '../dist/runtime/types/index.js'
|
|
8
10
|
|
|
9
11
|
export { default } from './module.mjs'
|
|
10
12
|
|