-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
858 additions
and
1,078 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
<script lang="ts" setup> | ||
import { computed } from "vue" | ||
import Wrapper from "../lib/Wrapper.vue" | ||
import type { ComponentOrTagName } from "../types" | ||
import type { | ||
SliceComponentType, | ||
SliceLike, | ||
SliceZoneComponents, | ||
SliceZoneLike, | ||
} from "./types" | ||
import { usePrismic } from "../usePrismic" | ||
import { TODOSliceComponent } from "./TODOSliceComponent" | ||
/** | ||
* Props for `<SliceZone />`. | ||
* | ||
* @typeParam TContext - Arbitrary data made available to all Slice components | ||
*/ | ||
export type SliceZoneProps<TContext = unknown> = { | ||
/** | ||
* List of Slice data from the Slice Zone. | ||
*/ | ||
slices: SliceZoneLike<SliceLike & Record<string, unknown>> | ||
/** | ||
* A record mapping Slice types to Vue components. | ||
*/ | ||
components?: SliceZoneComponents | ||
/** | ||
* The Vue component rendered if a component mapping from the `components` | ||
* prop cannot be found. | ||
* | ||
* @remarks | ||
* Components will be rendered using the {@link SliceComponentProps} interface. | ||
* | ||
* @defaultValue The Slice Zone default component provided to `@prismicio/vue` plugin if configured, otherwise `null` when `process.env.NODE_ENV === "production"` else {@link TODOSliceComponent}. | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
defaultComponent?: SliceComponentType<any, TContext> | ||
/** | ||
* Arbitrary data made available to all Slice components. | ||
*/ | ||
context?: TContext | ||
/** | ||
* An HTML tag name or a component used to wrap the output. `<SliceZone />` is | ||
* not wrapped by default. | ||
* | ||
* @defaultValue `"template"` (no wrapper) | ||
*/ | ||
wrapper?: ComponentOrTagName | ||
} | ||
const props = defineProps<SliceZoneProps>() | ||
defineOptions({ name: "SliceZone" }) | ||
const { options } = usePrismic() | ||
const renderedSlices = computed(() => { | ||
return props.slices.map((slice, index) => { | ||
const type = | ||
"slice_type" in slice ? (slice.slice_type as string) : slice.type | ||
const key = | ||
"id" in slice && typeof slice.id === "string" | ||
? slice.id | ||
: `${index}-${JSON.stringify(slice)}` | ||
const is = | ||
props.components?.[type] || | ||
props.defaultComponent || | ||
options.components?.sliceZoneDefaultComponent | ||
if (!is) { | ||
return { is: TODOSliceComponent, key, props: { slice } } | ||
} | ||
if (slice.__mapped) { | ||
const { __mapped, ...mappedProps } = slice | ||
return { is, key, props: mappedProps } | ||
} | ||
return { | ||
is, | ||
key, | ||
props: { | ||
slice, | ||
index, | ||
context: props.context, | ||
slices: props.slices, | ||
}, | ||
} | ||
}) | ||
}) | ||
</script> | ||
|
||
<template> | ||
<Wrapper v-if="slices" :wrapper="wrapper"> | ||
<Component | ||
v-for="renderedSlice in renderedSlices" | ||
:is="renderedSlice.is" | ||
:key="renderedSlice.key" | ||
v-bind="renderedSlice.props" | ||
/> | ||
</Wrapper> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import type { PropType } from "vue" | ||
import { computed, defineComponent, h, watchEffect } from "vue" | ||
|
||
import type { SliceComponentType, SliceLike } from "./types" | ||
|
||
/** | ||
* This Slice component can be used as a reminder to provide a proper | ||
* implementation. | ||
* | ||
* This is also the default Vue component rendered when a component mapping | ||
* cannot be found in `<SliceZone />`. | ||
*/ | ||
export const TODOSliceComponent = | ||
typeof process !== "undefined" && process.env.NODE_ENV === "development" | ||
? /*#__PURE__*/ (defineComponent({ | ||
name: "TODOSliceComponent", | ||
props: { | ||
slice: { | ||
type: Object as PropType<SliceLike>, | ||
required: true, | ||
}, | ||
}, | ||
setup(props) { | ||
const type = computed(() => { | ||
return "slice_type" in props.slice | ||
? props.slice.slice_type | ||
: props.slice.type | ||
}) | ||
|
||
watchEffect(() => { | ||
console.warn( | ||
`[SliceZone] Could not find a component for Slice type "${type.value}"`, | ||
props.slice, | ||
) | ||
}) | ||
|
||
return () => { | ||
return h( | ||
"section", | ||
{ | ||
"data-slice-zone-todo-component": "", | ||
"data-slice-type": type.value, | ||
}, | ||
[`Could not find a component for Slice type "${type.value}"`], | ||
) | ||
} | ||
}, | ||
}) as SliceComponentType) | ||
: ((() => null) as SliceComponentType) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { markRaw } from "vue" | ||
|
||
import type { | ||
SliceComponentType, | ||
SliceLike, | ||
SliceZoneComponents, | ||
} from "./types" | ||
|
||
/** | ||
* Gets an optimized record of Slice types mapped to Vue components. Each | ||
* components will be rendered for each instance of their Slice type. | ||
* | ||
* @remarks | ||
* This is essentially an helper function to ensure {@link markRaw} is correctly | ||
* applied on each components, improving performances. | ||
* | ||
* @example | ||
* | ||
* ```javascript | ||
* // Defining a slice components | ||
* import { defineSliceZoneComponents } from "@prismicio/vue"; | ||
* | ||
* export default { | ||
* data() { | ||
* components: defineSliceZoneComponents({ | ||
* foo: Foo, | ||
* bar: defineAsyncComponent( | ||
* () => new Promise((res) => res(Bar)), | ||
* ), | ||
* baz: "Baz", | ||
* }), | ||
* } | ||
* }; | ||
* ``` | ||
* | ||
* @typeParam TSlice - The type(s) of slices in the Slice Zone | ||
* @typeParam TContext - Arbitrary data made available to all Slice components | ||
* | ||
* @param components - {@link SliceZoneComponents} | ||
* | ||
* @returns A new optimized record of {@link SliceZoneComponents} | ||
*/ | ||
export const defineSliceZoneComponents = < | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
TSlice extends SliceLike = any, | ||
TContext = unknown, | ||
>( | ||
components: SliceZoneComponents<TSlice, TContext>, | ||
): SliceZoneComponents<TSlice, TContext> => { | ||
const result = {} as SliceZoneComponents<TSlice, TContext> | ||
|
||
let type: keyof typeof components | ||
for (type in components) { | ||
const component = components[type] | ||
result[type] = | ||
typeof component === "string" | ||
? component | ||
: markRaw( | ||
component as SliceComponentType< | ||
Extract<TSlice, SliceLike<typeof type>>, | ||
TContext | ||
>, | ||
) | ||
} | ||
|
||
return result | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import type { PropType } from "vue" | ||
|
||
import type { SliceComponentProps, SliceLike } from "./types" | ||
|
||
/** | ||
* Native Vue props for a component rendering content from a Prismic Slice using | ||
* the `<SliceZone />` component. | ||
* | ||
* @typeParam TSlice - The Slice type | ||
* @typeParam TContext - Arbitrary data passed to `<SliceZone />` and made | ||
* available to all Slice components | ||
*/ | ||
export type DefineComponentSliceComponentProps< | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
TSlice extends SliceLike = any, | ||
TContext = unknown, | ||
> = { | ||
slice: { | ||
type: PropType<SliceComponentProps<TSlice, TContext>["slice"]> | ||
required: true | ||
} | ||
index: { | ||
type: PropType<SliceComponentProps<TSlice, TContext>["index"]> | ||
required: true | ||
} | ||
slices: { | ||
type: PropType<SliceComponentProps<TSlice, TContext>["slices"]> | ||
required: true | ||
} | ||
context: { | ||
type: PropType<SliceComponentProps<TSlice, TContext>["context"]> | ||
required: true | ||
} | ||
} | ||
|
||
/** | ||
* Gets native Vue props for a component rendering content from a Prismic Slice | ||
* using the `<SliceZone />` component. | ||
* | ||
* Props are: `["slice", "index", "slices", "context"]` | ||
* | ||
* @example | ||
* | ||
* ```javascript | ||
* // Defining a new slice component | ||
* import { getSliceComponentProps } from "@prismicio/vue" | ||
* | ||
* export default { | ||
* props: getSliceComponentProps(), | ||
* } | ||
* ``` | ||
* | ||
* @example | ||
* | ||
* ```javascript | ||
* // Defining a new slice component with visual hint | ||
* import { getSliceComponentProps } from "@prismicio/vue" | ||
* | ||
* export default { | ||
* props: getSliceComponentProps(["slice", "index", "slices", "context"]), | ||
* } | ||
* ``` | ||
* | ||
* @typeParam TSlice - The Slice type | ||
* @typeParam TContext - Arbitrary data passed to `<SliceZone />` and made | ||
* available to all Slice components | ||
* | ||
* @param propsHint - An optional array of prop names used for the sole purpose | ||
* of having a visual hint of which props are made available to the slice, | ||
* this parameters doesn't have any effect | ||
* | ||
* @returns Props object to use with {@link defineComponent} | ||
*/ | ||
export const getSliceComponentProps = < | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
TSlice extends SliceLike = any, | ||
TContext = unknown, | ||
>( | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
propsHint?: ["slice", "index", "slices", "context"], | ||
): DefineComponentSliceComponentProps<TSlice, TContext> => ({ | ||
slice: { | ||
type: Object as PropType<SliceComponentProps<TSlice, TContext>["slice"]>, | ||
required: true, | ||
}, | ||
index: { | ||
type: Number as PropType<SliceComponentProps<TSlice, TContext>["index"]>, | ||
required: true, | ||
}, | ||
slices: { | ||
type: Array as PropType<SliceComponentProps<TSlice, TContext>["slices"]>, | ||
required: true, | ||
}, | ||
context: { | ||
type: null as unknown as PropType< | ||
SliceComponentProps<TSlice, TContext>["context"] | ||
>, | ||
required: true, | ||
}, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
export { TODOSliceComponent } from "./TODOSliceComponent" | ||
|
||
export type { DefineComponentSliceComponentProps } from "./getSliceComponentProps" | ||
export { getSliceComponentProps } from "./getSliceComponentProps" | ||
|
||
export { defineSliceZoneComponents } from "./defineSliceZoneComponents" | ||
|
||
export type { | ||
SliceComponentProps, | ||
SliceComponentType, | ||
SliceLike, | ||
SliceLikeGraphQL, | ||
SliceLikeRestV2, | ||
SliceZoneComponents, | ||
SliceZoneLike, | ||
} from "./types" |
Oops, something went wrong.