@platecms/delta-vue 0.6.0 → 0.7.1

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.
Files changed (62) hide show
  1. package/.env.example +7 -0
  2. package/__generated__/fragment-masking.ts +87 -0
  3. package/__generated__/gql.ts +24 -0
  4. package/__generated__/graphql.ts +3125 -0
  5. package/__generated__/index.ts +2 -0
  6. package/codegen.config.ts +23 -0
  7. package/eslint.config.mjs +43 -0
  8. package/package.json +6 -6
  9. package/project.json +54 -0
  10. package/src/components/ExperienceComponent.vue +26 -0
  11. package/src/components/GridPlacement.vue +16 -0
  12. package/src/components/RenderLibraryComponent.vue +30 -0
  13. package/src/components/RootExperienceComponentProvider.vue +16 -0
  14. package/src/components/atoms/icon/FontAwesomeIcon.vue +21 -0
  15. package/src/components/editor/EditorExperienceComponent.vue +36 -0
  16. package/src/components/editor/EditorGridPlacement.vue +86 -0
  17. package/src/components/editor/EditorRootExperienceComponent.vue +17 -0
  18. package/src/components/editor/controls/EditorControlsContainer.vue +120 -0
  19. package/src/components/placeholders/EmptyGridPlacements.vue +62 -0
  20. package/src/components/placeholders/MissingExperienceComponent.vue +21 -0
  21. package/src/createDelta.ts +16 -0
  22. package/src/defineComponent.ts +37 -0
  23. package/src/index.ts +69 -0
  24. package/src/router/index.ts +19 -0
  25. package/src/styles.css +1 -0
  26. package/src/views/ExperienceEditorView.vue +137 -0
  27. package/src/views/ExperiencePreviewView.vue +32 -0
  28. package/src/views/ExperienceView.vue +61 -0
  29. package/src/views/NotFoundView.vue +27 -0
  30. package/src/vue-shims.d.ts +5 -0
  31. package/tsconfig.json +25 -0
  32. package/tsconfig.lib.json +25 -0
  33. package/tsconfig.spec.json +22 -0
  34. package/vite.config.ts +59 -0
  35. package/ExperienceEditorView-EkCJdeah.js +0 -4
  36. package/ExperienceEditorView-RJb7Vinf.cjs +0 -1
  37. package/ExperiencePreviewView-BnPvMCAn.js +0 -4
  38. package/ExperiencePreviewView-Bx0rYgZ0.cjs +0 -1
  39. package/ExperienceView-C0KQHjrZ.js +0 -4
  40. package/ExperienceView-DJZtUHn2.cjs +0 -1
  41. package/components/ExperienceComponent.vue.d.ts +0 -22
  42. package/components/GridPlacement.vue.d.ts +0 -6
  43. package/components/RenderLibraryComponent.vue.d.ts +0 -26
  44. package/components/RootExperienceComponentProvider.vue.d.ts +0 -21
  45. package/components/atoms/icon/FontAwesomeIcon.vue.d.ts +0 -8
  46. package/components/editor/EditorExperienceComponent.vue.d.ts +0 -9
  47. package/components/editor/EditorGridPlacement.vue.d.ts +0 -10
  48. package/components/editor/EditorRootExperienceComponent.vue.d.ts +0 -7
  49. package/components/editor/controls/EditorControlsContainer.vue.d.ts +0 -24
  50. package/components/placeholders/EmptyGridPlacements.vue.d.ts +0 -9
  51. package/components/placeholders/MissingExperienceComponent.vue.d.ts +0 -6
  52. package/createDelta.d.ts +0 -2
  53. package/defineComponent.d.ts +0 -10
  54. package/index.cjs +0 -53
  55. package/index.css +0 -1
  56. package/index.d.ts +0 -31
  57. package/index.js +0 -10869
  58. package/router/index.d.ts +0 -4
  59. package/views/ExperienceEditorView.vue.d.ts +0 -2
  60. package/views/ExperiencePreviewView.vue.d.ts +0 -2
  61. package/views/ExperienceView.vue.d.ts +0 -2
  62. package/views/NotFoundView.vue.d.ts +0 -2
@@ -0,0 +1,2 @@
1
+ export * from "./fragment-masking";
2
+ export * from "./gql";
@@ -0,0 +1,23 @@
1
+ import { CodegenConfig } from "@graphql-codegen/cli";
2
+
3
+ if (import.meta.env.VITE_OATHKEEPER_ENDPOINT === undefined || import.meta.env.CODEGEN_COOKIE === undefined) {
4
+ throw new Error("Missing 'VITE_OATHKEEPER_ENDPOINT' and/or 'CODEGEN_COOKIE' environment variables");
5
+ }
6
+
7
+ const CONFIG: CodegenConfig = {
8
+ overwrite: true,
9
+ schema: {
10
+ [import.meta.env.VITE_OATHKEEPER_ENDPOINT]: {
11
+ headers: {
12
+ Cookie: import.meta.env.CODEGEN_COOKIE,
13
+ },
14
+ },
15
+ },
16
+ generates: {
17
+ "./packages/delta-vue/__generated__/": {
18
+ preset: "client",
19
+ },
20
+ },
21
+ };
22
+
23
+ export default CONFIG;
@@ -0,0 +1,43 @@
1
+ import {
2
+ defineConfigWithVueTs,
3
+ vueTsConfigs,
4
+ } from '@vue/eslint-config-typescript'
5
+ import { globalIgnores } from 'eslint/config'
6
+ import { FlatCompat } from "@eslint/eslintrc";
7
+ import { dirname } from "path";
8
+ import { fileURLToPath } from "url";
9
+ import baseConfig from "../../eslint.base.config.mjs";
10
+ import js from "@eslint/js";
11
+ import pluginVue from 'eslint-plugin-vue'
12
+
13
+ const compat = new FlatCompat({
14
+ baseDirectory: dirname(fileURLToPath(import.meta.url)),
15
+ recommendedConfig: js.configs.recommended,
16
+ });
17
+
18
+ export default defineConfigWithVueTs(
19
+ js.configs.recommended,
20
+ ...baseConfig,
21
+ pluginVue.configs['flat/essential'],
22
+ vueTsConfigs.recommended,
23
+ ...compat.extends(
24
+ "@vue/eslint-config-prettier/skip-formatting",
25
+ ),
26
+ {
27
+ languageOptions: {
28
+ parserOptions: {
29
+ project: "packages/delta-vue/tsconfig.*?.json",
30
+ },
31
+ },
32
+ },
33
+ {
34
+ files: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.vue"],
35
+ rules: {
36
+ "vue/multi-word-component-names": "off",
37
+ "@typescript-eslint/naming-convention": "off",
38
+ "@typescript-eslint/strict-boolean-expressions": "off",
39
+ "@typescript-eslint/no-use-before-define": "off",
40
+ },
41
+ },
42
+ globalIgnores(["**/dist", "__generated__/**/*", "**/vite.config.*.timestamp*", "**/vitest.config.*.timestamp*", "package.json", "**/*.json"]),
43
+ )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platecms/delta-vue",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Plugin to connect to the Plate Delta CMS in a Vue application.",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {
@@ -23,10 +23,10 @@
23
23
  "./style.css": "./style.css"
24
24
  },
25
25
  "dependencies": {
26
- "@platecms/delta-cast": "0.6.0",
27
- "@platecms/delta-castscript": "0.6.0",
28
- "@platecms/delta-client": "0.6.0",
29
- "@platecms/delta-plate-resource-notation": "0.6.0",
26
+ "@platecms/delta-cast": "0.7.0",
27
+ "@platecms/delta-castscript": "0.7.0",
28
+ "@platecms/delta-client": "0.7.0",
29
+ "@platecms/delta-plate-resource-notation": "0.7.0",
30
30
  "@graphql-codegen/cli": "5.0.7",
31
31
  "@graphql-typed-document-node/core": "3.2.0",
32
32
  "axios": "1.11.0",
@@ -47,4 +47,4 @@
47
47
  "@apollo/client": "3.13.9",
48
48
  "defu": "6.1.4"
49
49
  }
50
- }
50
+ }
package/project.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "delta-vue",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "packages/delta-vue/src",
5
+ "projectType": "library",
6
+ "tags": [
7
+ "package",
8
+ "delta-vue"
9
+ ],
10
+ "// targets": "to see all targets run: nx show project @platecms/delta-vue --web",
11
+ "targets": {
12
+ "codegen": {
13
+ "outputs": [
14
+ "{projectRoot}/app/graphql/generated.ts"
15
+ ],
16
+ "command": "graphql-codegen --config {projectRoot}/codegen.config.ts"
17
+ },
18
+ "build": {
19
+ "executor": "@nx/vite:build",
20
+ "outputs": [
21
+ "{options.outputPath}"
22
+ ],
23
+ "defaultConfiguration": "production",
24
+ "options": {
25
+ "outputPath": "generated/dist/{projectRoot}"
26
+ },
27
+ "configurations": {
28
+ "development": {
29
+ "mode": "development"
30
+ },
31
+ "production": {
32
+ "mode": "production"
33
+ }
34
+ }
35
+ },
36
+ "deploy": {
37
+ "executor": "nx:run-commands",
38
+ "dependsOn": [
39
+ "build"
40
+ ],
41
+ "configurations": {
42
+ "dev": {
43
+ "command": "is-ci && echo 'Not deployed on dev.'"
44
+ },
45
+ "acc": {
46
+ "command": "echo 'Packages will be published via nx release publish'"
47
+ },
48
+ "prod": {
49
+ "command": "is-ci && echo 'Not deployed on prod.'"
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,26 @@
1
+ <script setup lang="ts">
2
+ import {sortBy} from "lodash";
3
+ import type {ExperienceComponent} from "../../__generated__/graphql";
4
+ import RenderLibraryComponent from "./RenderLibraryComponent.vue";
5
+ import GridPlacement from "./GridPlacement.vue";
6
+
7
+ defineProps<{
8
+ experienceComponent: ExperienceComponent
9
+ }>()
10
+ </script>
11
+
12
+ <template>
13
+ <slot name="render-component">
14
+ <RenderLibraryComponent
15
+ :building-block="experienceComponent.buildingBlock"
16
+ :building-block-field-fulfillments="experienceComponent.buildingBlockFieldFulfillments"
17
+ />
18
+ </slot>
19
+
20
+ <slot name="grid-placements">
21
+ <GridPlacement v-for="gridPlacement in sortBy(experienceComponent.grid?.gridPlacements, 'row')" :grid-placement="gridPlacement" :key="gridPlacement.prn" />
22
+ </slot>
23
+ </template>
24
+
25
+ <style scoped>
26
+ </style>
@@ -0,0 +1,16 @@
1
+ <script setup lang="ts">
2
+ import type {GridPlacement} from "../../__generated__/graphql";
3
+ import ExperienceComponent from "./ExperienceComponent.vue";
4
+
5
+ defineProps<{
6
+ gridPlacement: GridPlacement
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <ExperienceComponent :experience-component="gridPlacement.experienceComponent" />
12
+ </template>
13
+
14
+ <style scoped>
15
+
16
+ </style>
@@ -0,0 +1,30 @@
1
+ <script lang="ts" setup>
2
+ import {type Component, computed, inject} from "vue";
3
+ import type {BuildingBlock, BuildingBlockFieldFulfillment} from "../../__generated__/graphql";
4
+
5
+ const library = inject<{ [key: string]: Component }>('library')
6
+
7
+ const props = withDefaults(
8
+ defineProps<{
9
+ buildingBlock?: BuildingBlock
10
+ buildingBlockFieldFulfillments?: BuildingBlockFieldFulfillment[]
11
+ }>(), {
12
+ buildingBlockFieldFulfillments: () => []
13
+ }
14
+ )
15
+
16
+ const currentComponent = computed(() => {
17
+ if(!library || !props.buildingBlock) return undefined;
18
+
19
+ return library[props.buildingBlock.slug]
20
+ })
21
+ </script>
22
+
23
+ <template>
24
+ <component v-if="currentComponent" :is="currentComponent" :building-block="buildingBlock" :building-block-field-fulfillments="buildingBlockFieldFulfillments" />
25
+ <slot name="missing-component" :data="buildingBlock" v-else-if="buildingBlock" />
26
+ </template>
27
+
28
+ <style scoped>
29
+
30
+ </style>
@@ -0,0 +1,16 @@
1
+ <script setup lang="ts">
2
+ import type {ExperienceComponent} from "../../__generated__/graphql";
3
+ import ExperienceComponentComponent from "./ExperienceComponent.vue";
4
+
5
+ defineProps<{
6
+ rootExperienceComponent: ExperienceComponent
7
+ }>();
8
+ </script>
9
+
10
+ <template>
11
+ <slot>
12
+ <ExperienceComponentComponent v-if="rootExperienceComponent" :experience-component="rootExperienceComponent" />
13
+ </slot>
14
+ </template>
15
+
16
+ <style scoped></style>
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue';
3
+
4
+ const props = defineProps<{
5
+ icon: [string, string]
6
+ className?: string
7
+ size?: string
8
+ spin?: boolean
9
+ }>()
10
+
11
+ const iconStyle = computed(() => {
12
+ return props.icon[0] === 'fas' ? 'fa-solid' : 'fa-light'
13
+ })
14
+
15
+ </script>
16
+
17
+ <template>
18
+ <i
19
+ :class="`${iconStyle} fa-${icon[1]} ${className ?? ''} ${size && size !== 'md' ? `text-${size}` : 'text-[16px]'} ${spin ? 'fa-spin' : ''}`"
20
+ ></i>
21
+ </template>
@@ -0,0 +1,36 @@
1
+ <script lang="ts" setup>
2
+ import {
3
+ type Draft,
4
+ } from "@platecms/delta-client/utils"
5
+ import {sortBy} from "lodash";
6
+ import type {ExperienceComponent, GridPlacement} from "../../../__generated__/graphql";
7
+ import {inject} from "vue";
8
+ import EditorControlsContainer from "./controls/EditorControlsContainer.vue";
9
+ import RenderLibraryComponent from "../RenderLibraryComponent.vue";
10
+ import MissingExperienceComponent from "../placeholders/MissingExperienceComponent.vue";
11
+ import EditorGridPlacement from "./EditorGridPlacement.vue";
12
+
13
+ defineProps<{
14
+ rows?: number
15
+ experienceComponent: Draft<ExperienceComponent>
16
+ gridPlacement?: Draft<GridPlacement>
17
+ }>()
18
+
19
+ const blacklist = inject<string[]>('blacklist', [])
20
+ </script>
21
+
22
+ <template>
23
+ <EditorControlsContainer v-if="experienceComponent?.buildingBlock && !blacklist.includes(experienceComponent.buildingBlock.slug)" :rows="rows" :row="gridPlacement?.row" :experience-component="experienceComponent">
24
+ <RenderLibraryComponent :building-block="experienceComponent.buildingBlock" :building-block-field-fulfillments="experienceComponent.buildingBlockFieldFulfillments">
25
+ <template v-slot:missing-component="slotProps">
26
+ <MissingExperienceComponent v-if="slotProps.data" :building-block="slotProps.data" />
27
+ </template>
28
+ </RenderLibraryComponent>
29
+ </EditorControlsContainer>
30
+
31
+ <EditorGridPlacement v-for="gridPlacement in sortBy(experienceComponent.grid?.gridPlacements, 'row')" :grid-placement="gridPlacement as Draft<GridPlacement>" :rows="experienceComponent?.grid?.gridPlacements.length" :key="gridPlacement.prn" />
32
+ </template>
33
+
34
+ <style scoped>
35
+
36
+ </style>
@@ -0,0 +1,86 @@
1
+ <script setup lang="ts">
2
+ import {throttle} from "lodash";
3
+ import {ref, inject} from "vue";
4
+ import type { Ref } from "vue";
5
+ import {WindowConnector, ConnectorEventType, type Draft} from "@platecms/delta-client/utils"
6
+ import type { ExperienceComponentCreateEvent } from "@platecms/delta-client/utils"
7
+ import type {BuildingBlock, ExperienceComponent, GridPlacement} from "../../../__generated__/graphql";
8
+ import RenderLibraryComponent from "../RenderLibraryComponent.vue";
9
+ import MissingExperienceComponent from "../placeholders/MissingExperienceComponent.vue";
10
+ import EditorExperienceComponent from "./EditorExperienceComponent.vue";
11
+
12
+ const props = defineProps<{
13
+ rows?: number
14
+ gridPlacement: Draft<GridPlacement>
15
+ }>()
16
+
17
+ const buildingBlockToCreate = inject<Ref<BuildingBlock | undefined>>('buildingBlockToCreate')
18
+ const connector = inject<WindowConnector>('connector')
19
+ const mode = inject<Ref<'edit' | 'drag'>>('mode')
20
+
21
+ const isInsideTarget = ref(false)
22
+
23
+ const mousePosition: Ref<'bottom' | 'top' | undefined> = ref(undefined)
24
+
25
+ const target = ref<HTMLElement | undefined>(undefined)
26
+
27
+
28
+ const updateMode = inject<(mode: 'edit' | 'drag' | 'preview') => void>('updateMode')
29
+ const throttleHandleMouseOver = throttle(handleMouseOver, 250)
30
+
31
+ function handleMouseOver(event: MouseEvent) {
32
+ if(!isInsideTarget.value || !target.value) return
33
+
34
+ const targetRectangle = target.value.getBoundingClientRect();
35
+
36
+ // const x = event.clientX - targetRectangle.left;
37
+ const y = event.clientY - targetRectangle.top;
38
+
39
+ mousePosition.value = y > Math.ceil(targetRectangle.height / 2) ? 'bottom' : 'top';
40
+ }
41
+
42
+ function handleMouseUp() {
43
+ if(mousePosition.value === undefined || mode?.value !== 'drag') return
44
+
45
+ const index = props.gridPlacement.row + (mousePosition.value === 'bottom' ? 1 : 0)
46
+
47
+ connector?.send({ type: ConnectorEventType.EXPERIENCE_COMPONENT_CREATE, payload: { index: index > -1 ? index : 0 } } as ExperienceComponentCreateEvent)
48
+ if(updateMode) updateMode('edit')
49
+ mousePosition.value = undefined
50
+ }
51
+ </script>
52
+
53
+ <template>
54
+ <div
55
+ ref="target"
56
+ @mouseup="handleMouseUp"
57
+ @mouseenter="() => isInsideTarget = true"
58
+ @mouseleave="() => {
59
+ isInsideTarget = false
60
+ mousePosition = undefined
61
+ }"
62
+ @mousemove="throttleHandleMouseOver"
63
+ >
64
+ <div v-if="isInsideTarget && mode === 'drag' && mousePosition === 'top'" class="dv:animate-pulse dv:outline-2 dv:outline-offset-2 dv:outline-gray-800 dv:outline-dashed">
65
+ <RenderLibraryComponent :building-block="buildingBlockToCreate">
66
+ <template v-slot:missing-component="slotProps">
67
+ <MissingExperienceComponent v-if="slotProps.data" :building-block="slotProps.data" />
68
+ </template>
69
+ </RenderLibraryComponent>
70
+ </div>
71
+
72
+ <EditorExperienceComponent :experience-component="gridPlacement.experienceComponent as Draft<ExperienceComponent>" :grid-placement="gridPlacement" :rows="rows" />
73
+
74
+ <div v-if="isInsideTarget && mode === 'drag' && mousePosition === 'bottom'" class="dv:animate-pulse dv:outline-2 dv:outline-offset-2 dv:outline-gray-800 dv:outline-dashed">
75
+ <RenderLibraryComponent :building-block="buildingBlockToCreate">
76
+ <template v-slot:missing-component="slotProps">
77
+ <MissingExperienceComponent v-if="slotProps.data" :building-block="slotProps.data" />
78
+ </template>
79
+ </RenderLibraryComponent>
80
+ </div>
81
+ </div>
82
+ </template>
83
+
84
+ <style scoped>
85
+
86
+ </style>
@@ -0,0 +1,17 @@
1
+ <script lang="ts" setup>
2
+ import type {ExperienceComponent} from "../../../__generated__/graphql";
3
+ import { type Draft } from "@platecms/delta-client/utils";
4
+ import EditorExperienceComponent from "./EditorExperienceComponent.vue";
5
+
6
+ defineProps<{
7
+ rootExperienceComponent: Draft<ExperienceComponent>
8
+ }>();
9
+ </script>
10
+
11
+ <template>
12
+ <EditorExperienceComponent v-if="rootExperienceComponent" :experience-component="rootExperienceComponent" />
13
+ </template>
14
+
15
+ <style scoped>
16
+
17
+ </style>
@@ -0,0 +1,120 @@
1
+ <script lang="ts" setup>
2
+ import {inject} from "vue";
3
+ import {
4
+ ConnectorEventType,
5
+ type Draft,
6
+ type ExperienceComponentEditEvent,
7
+ type ExperienceComponentRemoveEvent,
8
+ GridPlacementDownEvent,
9
+ GridPlacementUpEvent,
10
+ WindowConnector
11
+ } from "@platecms/delta-client/utils"
12
+ import type {ExperienceComponent} from "../../../../__generated__/graphql";
13
+ import FontAwesomeIcon from '../../atoms/icon/FontAwesomeIcon.vue';
14
+
15
+ const connector = inject<WindowConnector>('connector')
16
+ const mode = inject<'edit' | 'drag' | 'preview'>('mode', 'preview')
17
+
18
+ defineProps<{
19
+ rows?: number
20
+ row?: number
21
+ experienceComponent: Draft<ExperienceComponent>
22
+ }>()
23
+ </script>
24
+
25
+ <template>
26
+ <div class="dv:relative dv:group/controls dv:hover:outline-2 dv:outline-gray-800 dv:hover:-outline-offset-2 dv:hover:bg-gray-200 hover:dv:outline-gray-800 hover:dv:outline-dashed dv:z-40 dv:cursor-pointer">
27
+ <slot />
28
+
29
+ <div class="dv:hidden dv:group-hover/controls:flex dv:absolute dv:z-50 dv:top-0 dv:w-full dv:items-center dv:justify-center">
30
+ <div class="dv:rounded-b-md dv:bg-gray-800 dv:px-6 dv:py-2 dv:text-white dv:text-xs">
31
+ <p>
32
+ {{ experienceComponent.buildingBlock?.slug ?? 'No slug' }}
33
+ </p>
34
+ </div>
35
+ </div>
36
+ <div v-if="experienceComponent.isDraft" class="delta-snapshot-ignore dv:absolute dv:top-4 dv:right-4">
37
+ <div class="dv:px-3 dv:h-8 dv:bg-gray-800 dv:text-white dv:text-xs dv:flex dv:flex-col dv:gap-2 dv:items-center dv:justify-center dv:rounded-full">
38
+ <p>
39
+ Draft
40
+ </p>
41
+ </div>
42
+ </div>
43
+ <div v-if="mode === 'edit'" class="delta-snapshot-ignore dv:absolute dv:left-4 dv:top-4 dv:flex dv:gap-2 dv:z-50">
44
+ <!-- Edit button -->
45
+ <div
46
+ @click="connector?.send({
47
+ type: ConnectorEventType.EXPERIENCE_COMPONENT_EDIT,
48
+ payload: {
49
+ uuid: experienceComponent.uuid,
50
+ prn: experienceComponent.prn
51
+ }
52
+ } as ExperienceComponentEditEvent)"
53
+ class="dv:opacity-0 dv:group-hover/controls:opacity-100 dv:transition-all dv:duration-300 dv:bg-gray-800 dv:w-8 dv:h-8 dv:flex dv:items-center dv:justify-center dv:rounded-sm dv:cursor-pointer"
54
+ >
55
+ <FontAwesomeIcon :icon="['fal', 'pencil']" />
56
+ </div>
57
+
58
+ <!-- Remove button -->
59
+ <div
60
+ @click="connector?.send({
61
+ type: ConnectorEventType.EXPERIENCE_COMPONENT_REMOVE,
62
+ payload: {
63
+ uuid: experienceComponent.uuid,
64
+ prn: experienceComponent.prn
65
+ }
66
+ } as ExperienceComponentRemoveEvent)"
67
+ class="delta-snapshot-ignore dv:opacity-0 dv:group-hover/controls:opacity-100 dv:transition-all dv:duration-300 dv:bg-gray-800 dv:w-8 dv:h-8 dv:flex dv:items-center dv:justify-center dv:rounded-sm dv:cursor-pointer"
68
+ >
69
+ <FontAwesomeIcon :icon="['fal', 'trash-can']" />
70
+ </div>
71
+
72
+ <!-- Move Up button -->
73
+ <div
74
+ v-if="(row ?? 1) > 1"
75
+ @click="connector?.send({
76
+ type: ConnectorEventType.GRID_PLACEMENT_UP,
77
+ payload: {
78
+ uuid: experienceComponent.uuid,
79
+ prn: experienceComponent.prn
80
+ }
81
+ } as GridPlacementUpEvent)"
82
+ class="dv:opacity-0 dv:group-hover/controls:opacity-100 dv:transition-all dv:duration-300 dv:bg-gray-800 dv:w-8 dv:h-8 dv:flex dv:items-center dv:justify-center dv:rounded-sm dv:cursor-pointer"
83
+ >
84
+ <FontAwesomeIcon :icon="['fal', 'arrow-up']" />
85
+ </div>
86
+
87
+ <!-- Move Down button -->
88
+ <div
89
+ v-if="(row ?? 1) <( (rows ?? 1))"
90
+ @click="connector?.send({
91
+ type: ConnectorEventType.GRID_PLACEMENT_DOWN,
92
+ payload: {
93
+ uuid: experienceComponent.uuid,
94
+ prn: experienceComponent.prn
95
+ }
96
+ } as GridPlacementDownEvent)"
97
+ class="dv:opacity-0 dv:group-hover/controls:opacity-100 dv:transition-all dv:duration-300 dv:bg-gray-800 dv:w-8 dv:h-8 dv:flex dv:items-center dv:justify-center dv:rounded-sm dv:cursor-pointer"
98
+ >
99
+ <FontAwesomeIcon :icon="['fal', 'arrow-down']" />
100
+ </div>
101
+
102
+ <div class="dv:opacity-0 dv:group-hover/controls:opacity-100 dv:transition-all dv:duration-300 dv:bg-gray-800 dv:w-8 dv:h-8 dv:flex dv:items-center dv:justify-center dv:rounded-sm dv:cursor-pointer">
103
+ <p class="text-white">
104
+ {{ row }}
105
+ </p>
106
+ </div>
107
+ </div>
108
+
109
+ <!-- Hidden element to create snapshots -->
110
+ <div class="delta-snapshot-ignore dv:absolute dv:left-4 dv:top-0 dv:h-full dv:flex dv:items-center dv:justify-center">
111
+ <div class="dv:opacity-0 dv:transition-all dv:duration-300 dv:bg-gray-800 dv:w-8 dv:h-8 dv:flex dv:items-center dv:justify-center dv:rounded-sm dv:cursor-pointer">
112
+ <FontAwesomeIcon :icon="['fal', 'arrow-from-line']" />
113
+ </div>
114
+ </div>
115
+ </div>
116
+ </template>
117
+
118
+ <style scoped>
119
+
120
+ </style>
@@ -0,0 +1,62 @@
1
+ <script lang="ts" setup>
2
+ import {
3
+ ConnectorEventType,
4
+ type ExperienceComponentCreateEvent,
5
+ WindowConnector
6
+ } from "@platecms/delta-client/utils"
7
+ import {type Component, computed, inject, ref} from "vue";
8
+ import type {BuildingBlock} from "../../../__generated__/graphql";
9
+ const props = withDefaults(defineProps<{
10
+ type?: 'content-experience' | 'blueprint',
11
+ buildingBlockToCreate?: BuildingBlock
12
+ }>(), {
13
+ type: 'content-experience'
14
+ })
15
+
16
+ const isInsidePlaceholder = ref(false)
17
+ const connector = inject<WindowConnector>('connector')
18
+ const library = inject<{ [key: string]: Component }>('library')
19
+
20
+ const currentComponent = computed(() => {
21
+ if(!library || !props.buildingBlockToCreate) return undefined;
22
+
23
+ return library[props.buildingBlockToCreate.slug]
24
+ })
25
+
26
+ function handleMouseUp() {
27
+ connector?.send({ type: ConnectorEventType.EXPERIENCE_COMPONENT_CREATE, payload: { index: 0 } } as ExperienceComponentCreateEvent)
28
+ isInsidePlaceholder.value = false
29
+ }
30
+ </script>
31
+
32
+ <template>
33
+ <div @mouseenter="isInsidePlaceholder = true" @mouseleave="isInsidePlaceholder = false" @mouseup="handleMouseUp()">
34
+ <component v-if="currentComponent && isInsidePlaceholder" :is="currentComponent" :building-block="buildingBlockToCreate" :building-block-field-fulfillments="[]" class="dv:animate-pulse" />
35
+
36
+ <div v-else class="dv:p-4 dv:h-screen dv:w-full">
37
+ <div class="dv:border dv:border-dashed dv:border-[#705ED9] dv:h-full dv:w-full dv:flex dv:flex-col dv:gap-4 dv:items-center dv:justify-center dv:bg-[#EAEAFA] dv:rounded-md">
38
+ <svg width="39" height="39" viewBox="0 0 39 39" fill="none" xmlns="http://www.w3.org/2000/svg">
39
+ <rect x="0.5" y="9.5" width="29" height="29" rx="4.5" stroke="#705ED9" stroke-dasharray="2 2"/>
40
+ <rect x="9.5" y="0.5" width="29" height="29" rx="4.5" fill="#EAEAFA" stroke="#705ED9"/>
41
+ </svg>
42
+
43
+ <p v-if="type === 'content-experience'" class="dv:text-center dv:text-[#705ED9]">
44
+ Drag experience components from the sidebar to begin <br>
45
+ or add a template
46
+ </p>
47
+
48
+ <p v-else class="dv:text-center dv:text-[#705ED9]">
49
+ Drag experience components from the sidebar to begin
50
+ </p>
51
+
52
+ <button v-if="type === 'content-experience'" @click="connector?.send({ type: ConnectorEventType.BLUEPRINT_OVERLAY_OPEN, payload: null })" class="dv:bg-gradient-to-bl dv:from-primary dv:to-primary-tint dv:px-6 dv:py-3 dv:rounded-md dv:text-white dv:font-medium">
53
+ Use template
54
+ </button>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </template>
59
+
60
+ <style scoped>
61
+
62
+ </style>
@@ -0,0 +1,21 @@
1
+ <script lang="ts" setup>
2
+ import type {BuildingBlock} from "../../../__generated__/graphql";
3
+
4
+ defineProps<{
5
+ buildingBlock: BuildingBlock
6
+ }>()
7
+ </script>
8
+
9
+ <template>
10
+ <div class="dv:w-full dv:p-4">
11
+ <div class="dv:h-64 dv:flex dv:items-center dv:justify-center dv:border-2 dv:border-dashed">
12
+ <p>
13
+ Missing component: {{ buildingBlock.slug }}
14
+ </p>
15
+ </div>
16
+ </div>
17
+ </template>
18
+
19
+ <style scoped>
20
+
21
+ </style>
@@ -0,0 +1,16 @@
1
+ import { Delta } from "./index";
2
+ import { Component, markRaw } from "vue";
3
+ import { DeltaComponent } from "./defineComponent";
4
+
5
+ export function createDelta(): Delta {
6
+ const delta: Delta = markRaw({
7
+ install(app) {
8
+ app.provide("delta", delta);
9
+ app.config.globalProperties.$delta = delta;
10
+ },
11
+
12
+ components: markRaw<Record<string, DeltaComponent<Component>>>({}),
13
+ });
14
+
15
+ return delta;
16
+ }