rimelight-components 2.1.93 → 2.1.95

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/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rimelight-components",
3
- "version": "2.1.93",
3
+ "version": "2.1.95",
4
4
  "docs": "https://rimelight.com/tools/rimelight-components",
5
5
  "configKey": "rimelightComponents",
6
6
  "compatibility": {
package/dist/module.mjs CHANGED
@@ -4,7 +4,7 @@ import { readdirSync } from 'node:fs';
4
4
  import { basename } from 'node:path';
5
5
 
6
6
  const name = "rimelight-components";
7
- const version = "2.1.93";
7
+ const version = "2.1.95";
8
8
  const homepage = "https://rimelight.com/tools/rimelight-components";
9
9
 
10
10
  const defaultOptions = {
@@ -72,7 +72,12 @@ const items = ref([
72
72
  <div :class="root({ class: rc.root })">
73
73
  <div :class="menuContainer({ class: rc.menuContainer })">
74
74
  <UDropdownMenu :items="items">
75
- <UButton icon="lucide:grip-vertical" variant="ghost" color="neutral" />
75
+ <UButton
76
+ icon="lucide:grip-vertical"
77
+ variant="ghost"
78
+ color="neutral"
79
+ class="drag-handle cursor-grab active:cursor-grabbing"
80
+ />
76
81
  </UDropdownMenu>
77
82
  </div>
78
83
 
@@ -1,16 +1,35 @@
1
1
  import type { Block } from "../../types/index.js";
2
2
  export interface BlockEditRendererProps {
3
3
  blocks: Block[];
4
+ containerId?: string | null;
4
5
  rc?: {
5
6
  root?: string;
6
7
  };
7
8
  }
9
+ type __VLS_Props = BlockEditRendererProps;
8
10
  export interface BlockEditRendererEmits {
11
+ start: [];
12
+ end: [];
13
+ change: [any];
9
14
  }
10
15
  export interface BlockEditRendererSlots {
11
16
  }
12
17
  type __VLS_Slots = BlockEditRendererSlots;
13
- declare const __VLS_base: import("vue").DefineComponent<BlockEditRendererProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<BlockEditRendererProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ type __VLS_ModelProps = {
19
+ 'blocks': Block[];
20
+ };
21
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
22
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
23
+ start: () => any;
24
+ end: () => any;
25
+ change: (args_0: any) => any;
26
+ "update:blocks": (value: Block[]) => any;
27
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
28
+ onStart?: (() => any) | undefined;
29
+ onEnd?: (() => any) | undefined;
30
+ onChange?: ((args_0: any) => any) | undefined;
31
+ "onUpdate:blocks"?: ((value: Block[]) => any) | undefined;
32
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
33
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
15
34
  declare const _default: typeof __VLS_export;
16
35
  export default _default;
@@ -1,21 +1,59 @@
1
1
  <script setup>
2
- import {} from "vue";
2
+ import { watch, ref, inject } from "vue";
3
+ import draggable from "vuedraggable";
3
4
  import { getBlockEditorComponent } from "../../internal/blockMapper";
4
5
  import { tv } from "../../internal/tv";
5
6
  import { useRC } from "../../composables";
6
- const { blocks, rc: rcProp } = defineProps({
7
+ const props = defineProps({
7
8
  blocks: { type: Array, required: true },
9
+ containerId: { type: [String, null], required: false },
8
10
  rc: { type: Object, required: false }
9
11
  });
10
- const emit = defineEmits([]);
12
+ const { rc: rcProp, containerId = null } = props;
13
+ const blocks = defineModel("blocks", { type: Array, ...{ required: true } });
14
+ const emit = defineEmits(["start", "end", "change"]);
11
15
  const slots = defineSlots();
12
16
  const { rc } = useRC("BlockEditRenderer", rcProp);
17
+ const rendererId = Math.random().toString(36).substring(7);
18
+ console.log("[BlockEditRenderer] Instance created with ID:", rendererId, "containerId:", containerId, "initial blocks:", blocks.value.length);
19
+ const editorApi = inject("block-editor-api");
20
+ const isDraggingOver = ref(false);
21
+ const dragCounter = ref(0);
22
+ const onDragEnter = () => {
23
+ dragCounter.value++;
24
+ isDraggingOver.value = true;
25
+ };
26
+ const onDragLeave = () => {
27
+ dragCounter.value--;
28
+ if (dragCounter.value <= 0) {
29
+ isDraggingOver.value = false;
30
+ dragCounter.value = 0;
31
+ }
32
+ };
33
+ const onDrop = () => {
34
+ dragCounter.value = 0;
35
+ isDraggingOver.value = false;
36
+ };
13
37
  const blockEditRendererStyles = tv({
14
38
  slots: {
15
- root: "flex flex-col gap-lg w-full"
39
+ root: "flex flex-col w-full min-h-32 transition-all border-l-2 border-neutral-200/50 p-4 pl-6 rounded-r-lg"
40
+ },
41
+ variants: {
42
+ isDraggingOver: {
43
+ true: {
44
+ root: "border-l-4 border-primary-500 bg-primary-50/30 ring-1 ring-primary-500/10 z-10"
45
+ }
46
+ }
16
47
  }
17
48
  });
18
49
  const { root } = blockEditRendererStyles();
50
+ watch(blocks, (newBlocks, oldBlocks) => {
51
+ console.log(`[BlockEditRenderer ${rendererId}] Blocks changed:`, {
52
+ oldCount: oldBlocks?.length || 0,
53
+ newCount: newBlocks?.length || 0,
54
+ blockIds: newBlocks?.map((b) => ({ id: b.id, type: b.type }))
55
+ });
56
+ }, { deep: true });
19
57
  const getComponent = (block) => {
20
58
  if (!block || !block.type || block.type.length === 0) {
21
59
  console.error("Block object is missing the critical 'type' field.");
@@ -28,40 +66,116 @@ const getComponent = (block) => {
28
66
  }
29
67
  return resolvedComponent;
30
68
  };
69
+ const handleStart = () => {
70
+ console.log(`[BlockEditRenderer ${rendererId}] Drag START - current blocks:`, blocks.value.length);
71
+ emit("start");
72
+ };
73
+ const handleEnd = () => {
74
+ console.log(`[BlockEditRenderer ${rendererId}] Drag END - current blocks:`, blocks.value.length);
75
+ emit("end");
76
+ };
77
+ const handleChange = (event) => {
78
+ console.log(`[BlockEditRenderer ${rendererId}] Drag CHANGE event:`, {
79
+ event,
80
+ added: event.added,
81
+ removed: event.removed,
82
+ moved: event.moved,
83
+ currentBlocks: blocks.value.length,
84
+ containerId,
85
+ blockIds: blocks.value.map((b) => ({ id: b.id, type: b.type }))
86
+ });
87
+ if (event.added) {
88
+ console.log(`[BlockEditRenderer ${rendererId}] Block ADDED to this container:`, event.added.element.id);
89
+ }
90
+ if (event.removed) {
91
+ console.log(`[BlockEditRenderer ${rendererId}] Block REMOVED from this container:`, event.removed.element.id);
92
+ }
93
+ if (event.moved) {
94
+ console.log(`[BlockEditRenderer ${rendererId}] Block MOVED within same container`);
95
+ }
96
+ emit("change", event);
97
+ };
31
98
  </script>
32
99
 
33
100
  <template>
34
- <div :class="root({ class: rc.root })">
35
- <UEmpty
36
- v-if="!blocks || blocks.length === 0"
37
- variant="naked"
38
- icon="lucide:blocks"
39
- title="No blocks found for this page."
40
- description="It looks like there isn't any content added to this page yet."
41
- />
42
- <template v-else>
43
- <template v-for="block in blocks" :key="block.id">
44
- <template v-if="getComponent(block)">
45
- <RCBlock :id="block.id" :type="block.type" class="w-full">
46
- <component
47
- :is="getComponent(block)"
48
- :id="block.id"
49
- v-bind="block.props"
50
- :type="block.type"
51
- class="w-full"
101
+ <div
102
+ :class="[root({ isDraggingOver }), rc.root, 'relative', { 'is-empty': blocks.length === 0 }]"
103
+ @dragenter="onDragEnter"
104
+ @dragover.prevent
105
+ @dragleave="onDragLeave"
106
+ @drop="onDrop"
107
+ >
108
+ <draggable
109
+ v-model="blocks"
110
+ item-key="id"
111
+ handle=".drag-handle"
112
+ :group="{ name: 'blocks', pull: true, put: true }"
113
+ :animation="200"
114
+ :force-fallback="true"
115
+ fallback-class="drag-fallback"
116
+ ghost-class="drag-placeholder"
117
+ @start="handleStart"
118
+ @end="handleEnd"
119
+ @change="handleChange"
120
+ class="flex flex-col w-full flex-1"
121
+ :class="[blocks && blocks.length > 0 ? 'gap-lg min-h-16 pb-32' : 'gap-0 min-h-32']"
122
+ >
123
+ <template #header>
124
+ <div
125
+ v-if="!blocks || blocks.length === 0"
126
+ class="w-full p-4 flex items-center justify-center transition-all rounded-lg border-2 border-transparent"
127
+ :class="[isDraggingOver ? 'bg-primary-50/50 border-dashed border-primary-500/50' : '']"
128
+ >
129
+ <UEmpty
130
+ icon="i-lucide-layers"
131
+ title="Empty Section"
132
+ description="This area has no blocks yet. Drag items here or click to add your first block."
133
+ variant="naked"
134
+ class="w-full"
135
+ :ui="{ root: 'transition-transform px-4', title: isDraggingOver ? 'text-primary-600 font-bold' : '' }"
136
+ :class="[isDraggingOver ? 'scale-[1.02]' : '']"
137
+ >
138
+ <template #actions>
139
+ <UButton
140
+ label="Add Block"
141
+ icon="i-lucide-plus"
142
+ color="neutral"
143
+ variant="subtle"
144
+ @click="editorApi?.openAddBlockModal()"
145
+ />
146
+ </template>
147
+ </UEmpty>
148
+ </div>
149
+ </template>
150
+
151
+ <template #item="{ element: block }">
152
+ <div class="w-full">
153
+ <template v-if="getComponent(block)">
154
+ <RCBlock :id="block.id" :type="block.type" class="w-full">
155
+ <component
156
+ :is="getComponent(block)"
157
+ :id="block.id"
158
+ v-bind="block.props"
159
+ :type="block.type"
160
+ class="w-full"
161
+ />
162
+ </RCBlock>
163
+ </template>
164
+ <template v-else>
165
+ <UAlert
166
+ color="error"
167
+ variant="subtle"
168
+ icon="lucide:octagon-alert"
169
+ title="Rendering Error"
170
+ :description="`Block component for type \'${block.type || 'UNKNOWN_OR_MISSING'}\' was not found. This block will be skipped or the type is invalid/empty.`"
52
171
  />
53
- </RCBlock>
54
- </template>
55
- <template v-else>
56
- <UAlert
57
- color="error"
58
- variant="subtle"
59
- icon="lucide:octagon-alert"
60
- title="Rendering Error"
61
- :description="`Block component for type \'${block.type || 'UNKNOWN_OR_MISSING'}\' was not found. This block will be skipped or the type is invalid/empty.`"
62
- />
63
- </template>
172
+ </template>
173
+ </div>
64
174
  </template>
65
- </template>
175
+ </draggable>
66
176
  </div>
67
177
  </template>
178
+
179
+ <style scoped>
180
+ :deep(.drag-placeholder){background:#3b82f6;border-radius:2px;height:4px;margin-bottom:8px;margin-top:8px;opacity:1;overflow:hidden}:deep(.drag-fallback){background-color:#fff;border-radius:.5rem;box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);opacity:.9;transform:scale(1.01);z-index:9999}:deep(.drag-placeholder>*){display:none!important}.is-empty :deep(.drag-placeholder){border:0!important;display:none!important;height:0!important;margin:0!important;padding:0!important;visibility:hidden!important}
181
+ </style>
@@ -1,16 +1,35 @@
1
1
  import type { Block } from "../../types/index.js";
2
2
  export interface BlockEditRendererProps {
3
3
  blocks: Block[];
4
+ containerId?: string | null;
4
5
  rc?: {
5
6
  root?: string;
6
7
  };
7
8
  }
9
+ type __VLS_Props = BlockEditRendererProps;
8
10
  export interface BlockEditRendererEmits {
11
+ start: [];
12
+ end: [];
13
+ change: [any];
9
14
  }
10
15
  export interface BlockEditRendererSlots {
11
16
  }
12
17
  type __VLS_Slots = BlockEditRendererSlots;
13
- declare const __VLS_base: import("vue").DefineComponent<BlockEditRendererProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<BlockEditRendererProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ type __VLS_ModelProps = {
19
+ 'blocks': Block[];
20
+ };
21
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
22
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
23
+ start: () => any;
24
+ end: () => any;
25
+ change: (args_0: any) => any;
26
+ "update:blocks": (value: Block[]) => any;
27
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
28
+ onStart?: (() => any) | undefined;
29
+ onEnd?: (() => any) | undefined;
30
+ onChange?: ((args_0: any) => any) | undefined;
31
+ "onUpdate:blocks"?: ((value: Block[]) => any) | undefined;
32
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
33
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
15
34
  declare const _default: typeof __VLS_export;
16
35
  export default _default;
@@ -1,12 +1,16 @@
1
1
  import type { Block } from "../../types/index.js";
2
2
  export interface BlockEditorProps {
3
3
  historyLimit?: number;
4
+ containerId?: string | null;
4
5
  rc?: {};
5
6
  }
6
7
  type __VLS_Props = BlockEditorProps;
7
8
  export interface BlockEditorEmits {
8
9
  save: [];
9
10
  mutation: [];
11
+ start: [];
12
+ end: [];
13
+ change: [any];
10
14
  }
11
15
  type __VLS_ModelProps = {
12
16
  modelValue: Block[];
@@ -18,11 +22,17 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
18
22
  canUndo: import("vue").ComputedRef<boolean>;
19
23
  canRedo: import("vue").ComputedRef<boolean>;
20
24
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
25
+ start: () => any;
26
+ end: () => any;
21
27
  save: () => any;
28
+ change: (args_0: any) => any;
22
29
  mutation: () => any;
23
30
  "update:modelValue": (value: Block[]) => any;
24
31
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
32
+ onStart?: (() => any) | undefined;
33
+ onEnd?: (() => any) | undefined;
25
34
  onSave?: (() => any) | undefined;
35
+ onChange?: ((args_0: any) => any) | undefined;
26
36
  onMutation?: (() => any) | undefined;
27
37
  "onUpdate:modelValue"?: ((value: Block[]) => any) | undefined;
28
38
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -1,14 +1,23 @@
1
1
  <script setup>
2
- import { provide, ref } from "vue";
2
+ import { provide, inject, ref, watch, nextTick } from "vue";
3
+ import { v7 as uuidv7 } from "uuid";
3
4
  import { useBlockEditor, useRC } from "../../composables";
4
5
  import {} from "../../utils/blocks";
5
6
  import { useI18n } from "vue-i18n";
6
- const { historyLimit, rc: rcProp } = defineProps({
7
+ function regenerateIds(block) {
8
+ block.id = uuidv7();
9
+ if (block.props && "children" in block.props && Array.isArray(block.props.children)) {
10
+ block.props.children.forEach((child) => regenerateIds(child));
11
+ }
12
+ }
13
+ const { historyLimit, containerId = null, rc: rcProp } = defineProps({
7
14
  historyLimit: { type: Number, required: false },
15
+ containerId: { type: [String, null], required: false },
8
16
  rc: { type: Object, required: false }
9
17
  });
10
18
  const blocks = defineModel({ type: Array, ...{ required: true } });
11
- const emit = defineEmits(["save", "mutation"]);
19
+ console.log("[BlockEditor] Component mounted with", blocks.value.length, "blocks");
20
+ const emit = defineEmits(["save", "mutation", "start", "end", "change"]);
12
21
  const { t } = useI18n();
13
22
  const { rc } = useRC("BlockEditor", rcProp);
14
23
  const {
@@ -17,6 +26,7 @@ const {
17
26
  duplicateBlock,
18
27
  updateBlockProps,
19
28
  insertBlock,
29
+ relocateBlock,
20
30
  undo,
21
31
  redo,
22
32
  canUndo,
@@ -37,20 +47,50 @@ provide("block-editor-api", {
37
47
  duplicateBlock,
38
48
  updateBlockProps,
39
49
  insertBlock,
50
+ relocateBlock,
40
51
  canUndo,
41
52
  canRedo,
42
53
  undo,
43
54
  redo,
44
55
  openAddBlockModal
45
56
  });
57
+ const handleDragStart = () => {
58
+ console.log("[BlockEditor] Drag started");
59
+ emit("start");
60
+ };
61
+ const handleDragEnd = async () => {
62
+ console.log("[BlockEditor] Drag ended");
63
+ emit("end");
64
+ emit("mutation");
65
+ };
66
+ const handleBlockChange = (event) => {
67
+ console.log("[BlockEditor] handleBlockChange:", event);
68
+ if (event.added) {
69
+ const block = event.added.element;
70
+ if (block) {
71
+ console.log("[BlockEditor] Regenerating IDs for added block:", block.id);
72
+ regenerateIds(block);
73
+ }
74
+ }
75
+ emit("change", event);
76
+ if (event.added || event.removed || event.moved) {
77
+ emit("mutation");
78
+ }
79
+ };
46
80
  defineExpose({ undo, redo, canUndo, canRedo });
47
81
  </script>
48
82
 
49
83
  <template>
50
84
  <div class="flex flex-col gap-8 w-full">
51
- <RCBlockEditRenderer :blocks="blocks" />
85
+ <RCBlockEditRenderer
86
+ v-model:blocks="blocks"
87
+ :container-id="containerId"
88
+ @start="handleDragStart"
89
+ @end="handleDragEnd"
90
+ @change="handleBlockChange"
91
+ />
52
92
 
53
- <div class="flex flex-col items-center justify-center gap-md p-sm">
93
+ <div v-if="blocks && blocks.length > 0" class="flex flex-col items-center justify-center gap-md p-sm">
54
94
  <UButton
55
95
  color="neutral"
56
96
  :label="t('page_editor.add_block', 'Add Block')"
@@ -1,12 +1,16 @@
1
1
  import type { Block } from "../../types/index.js";
2
2
  export interface BlockEditorProps {
3
3
  historyLimit?: number;
4
+ containerId?: string | null;
4
5
  rc?: {};
5
6
  }
6
7
  type __VLS_Props = BlockEditorProps;
7
8
  export interface BlockEditorEmits {
8
9
  save: [];
9
10
  mutation: [];
11
+ start: [];
12
+ end: [];
13
+ change: [any];
10
14
  }
11
15
  type __VLS_ModelProps = {
12
16
  modelValue: Block[];
@@ -18,11 +22,17 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
18
22
  canUndo: import("vue").ComputedRef<boolean>;
19
23
  canRedo: import("vue").ComputedRef<boolean>;
20
24
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
25
+ start: () => any;
26
+ end: () => any;
21
27
  save: () => any;
28
+ change: (args_0: any) => any;
22
29
  mutation: () => any;
23
30
  "update:modelValue": (value: Block[]) => any;
24
31
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
32
+ onStart?: (() => any) | undefined;
33
+ onEnd?: (() => any) | undefined;
25
34
  onSave?: (() => any) | undefined;
35
+ onChange?: ((args_0: any) => any) | undefined;
26
36
  onMutation?: (() => any) | undefined;
27
37
  "onUpdate:modelValue"?: ((value: Block[]) => any) | undefined;
28
38
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -1,10 +1,10 @@
1
1
  <script setup>
2
- import { inject, computed } from "vue";
2
+ import { inject, computed, ref, watch, nextTick, shallowRef } from "vue";
3
3
  import { useI18n } from "vue-i18n";
4
4
  import { useAppConfig } from "#imports";
5
5
  import { tv } from "../../../internal/tv";
6
6
  import { useRC } from "../../../composables";
7
- const { id, variant, children, to, target, rc: rcProp } = defineProps({
7
+ const props = defineProps({
8
8
  id: { type: String, required: true },
9
9
  rc: { type: Object, required: false },
10
10
  variant: { type: String, required: true },
@@ -12,6 +12,7 @@ const { id, variant, children, to, target, rc: rcProp } = defineProps({
12
12
  to: { type: String, required: false },
13
13
  target: { type: String, required: false }
14
14
  });
15
+ const { rc: rcProp } = props;
15
16
  const emit = defineEmits([]);
16
17
  const slots = defineSlots();
17
18
  const { rc } = useRC("CalloutBlockEditor", rcProp);
@@ -33,8 +34,8 @@ const variants = [
33
34
  ];
34
35
  const appConfig = useAppConfig();
35
36
  const { t } = useI18n();
36
- const getVariantConfig = (variant2) => {
37
- return appConfig.rimelightComponents?.callouts?.[variant2] ?? {
37
+ const getVariantConfig = (variant) => {
38
+ return appConfig.rimelightComponents?.callouts?.[variant] ?? {
38
39
  icon: "lucide:alert-circle",
39
40
  title: "Callout",
40
41
  tooltip: "Callout"
@@ -49,17 +50,30 @@ const items = computed(() => [
49
50
  description: t(config.description || config.tooltip),
50
51
  onSelect: () => {
51
52
  if (editorApi) {
52
- editorApi.updateBlockProps(id, { variant: type });
53
+ editorApi.updateBlockProps(props.id, { variant: type });
53
54
  }
54
55
  }
55
56
  };
56
57
  })
57
58
  ]);
59
+ const localChildren = computed({
60
+ get: () => props.children ?? [],
61
+ set: (newChildren) => {
62
+ console.log("[CalloutBlockEditor] localChildren setter called:", newChildren.length);
63
+ if (editorApi && props.id) {
64
+ const childrenCopy = JSON.parse(JSON.stringify(newChildren));
65
+ editorApi.updateBlockProps(props.id, { children: childrenCopy });
66
+ }
67
+ }
68
+ });
69
+ const handleChildrenMutation = () => {
70
+ console.log("[CalloutBlockEditor] Mutation event received (handled by setter)");
71
+ };
58
72
  </script>
59
73
 
60
74
  <template>
61
75
  <div :class="root({ class: rc.root })">
62
- <RCCallout :variant="variant" :to="to" :target="target">
76
+ <RCCallout :variant="props.variant" :to="props.to" :target="props.target">
63
77
  <template #leading="{ icon, iconClass }">
64
78
  <UDropdownMenu :items="items" :ui="{ content: 'w-64' }">
65
79
  <template #item="{ item }">
@@ -79,7 +93,16 @@ const items = computed(() => [
79
93
  />
80
94
  </UDropdownMenu>
81
95
  </template>
82
- <RCBlockViewRenderer :blocks="children" />
96
+ <template #default>
97
+ <div class="w-full mt-2">
98
+ <RCBlockEditor
99
+ v-model="localChildren"
100
+ :container-id="props.id"
101
+ @mutation="handleChildrenMutation"
102
+ @end="handleChildrenMutation"
103
+ />
104
+ </div>
105
+ </template>
83
106
  </RCCallout>
84
107
  </div>
85
108
  </template>
@@ -1,9 +1,9 @@
1
1
  <script setup>
2
- import { inject, ref, computed, watch } from "vue";
2
+ import { inject, ref, computed, watch, nextTick, shallowRef } from "vue";
3
3
  import {} from "../../../types";
4
4
  import { tv } from "../../../internal/tv";
5
5
  import { useRC } from "../../../composables";
6
- const { level, title, description, children, id, rc: rcProp } = defineProps({
6
+ const props = defineProps({
7
7
  id: { type: String, required: true },
8
8
  rc: { type: Object, required: false },
9
9
  level: { type: Number, required: true },
@@ -11,6 +11,7 @@ const { level, title, description, children, id, rc: rcProp } = defineProps({
11
11
  description: { type: String, required: false },
12
12
  children: { type: Array, required: true }
13
13
  });
14
+ const { rc: rcProp } = props;
14
15
  const emit = defineEmits([]);
15
16
  const slots = defineSlots();
16
17
  const { rc } = useRC("SectionBlockEditor", rcProp);
@@ -22,11 +23,10 @@ const sectionBlockEditorStyles = tv({
22
23
  }
23
24
  });
24
25
  const { root, headerContainer, titleInput } = sectionBlockEditorStyles();
25
- const hasChildren = computed(() => children && children.length > 0);
26
26
  const editorApi = inject("block-editor-api");
27
- const localLevel = ref(level);
28
- const localTitle = ref(title);
29
- const localDescription = ref(description);
27
+ const localLevel = ref(props.level);
28
+ const localTitle = ref(props.title);
29
+ const localDescription = ref(props.description);
30
30
  const levelItems = [
31
31
  { label: "H1", value: 1 },
32
32
  { label: "H2", value: 2 },
@@ -36,25 +36,25 @@ const updateLocalTitle = (e) => {
36
36
  localTitle.value = e.target.value;
37
37
  };
38
38
  const commitTitleOnBlur = () => {
39
- if (editorApi && id && localTitle.value !== title) {
40
- editorApi.updateBlockProps(id, { title: localTitle.value });
39
+ if (editorApi && props.id && localTitle.value !== props.title) {
40
+ editorApi.updateBlockProps(props.id, { title: localTitle.value });
41
41
  }
42
42
  };
43
43
  const updateLocalDescription = (e) => {
44
44
  localDescription.value = e.target.value;
45
45
  };
46
46
  const commitDescriptionOnBlur = () => {
47
- if (editorApi && id && localDescription.value !== description) {
48
- editorApi.updateBlockProps(id, { description: localDescription.value });
47
+ if (editorApi && props.id && localDescription.value !== props.description) {
48
+ editorApi.updateBlockProps(props.id, { description: localDescription.value });
49
49
  }
50
50
  };
51
51
  watch(localLevel, (newLocalLevel) => {
52
- if (editorApi && id && newLocalLevel !== level) {
53
- editorApi.updateBlockProps(id, { level: newLocalLevel });
52
+ if (editorApi && props.id && newLocalLevel !== props.level) {
53
+ editorApi.updateBlockProps(props.id, { level: newLocalLevel });
54
54
  }
55
55
  });
56
56
  watch(
57
- () => title,
57
+ () => props.title,
58
58
  (newVal) => {
59
59
  if (newVal !== localTitle.value) {
60
60
  localTitle.value = newVal;
@@ -62,7 +62,7 @@ watch(
62
62
  }
63
63
  );
64
64
  watch(
65
- () => level,
65
+ () => props.level,
66
66
  (newVal) => {
67
67
  if (newVal !== localLevel.value) {
68
68
  localLevel.value = newVal;
@@ -70,13 +70,25 @@ watch(
70
70
  }
71
71
  );
72
72
  watch(
73
- () => description,
73
+ () => props.description,
74
74
  (newVal) => {
75
75
  if (newVal !== localDescription.value) {
76
76
  localDescription.value = newVal;
77
77
  }
78
78
  }
79
79
  );
80
+ const localChildren = computed({
81
+ get: () => props.children ?? [],
82
+ set: (newChildren) => {
83
+ if (editorApi && props.id) {
84
+ const childrenCopy = JSON.parse(JSON.stringify(newChildren));
85
+ editorApi.updateBlockProps(props.id, { children: childrenCopy });
86
+ }
87
+ }
88
+ });
89
+ const handleChildrenMutation = () => {
90
+ console.log("[SectionBlockEditor] Mutation event received (handled by setter)");
91
+ };
80
92
  </script>
81
93
 
82
94
  <template>
@@ -114,7 +126,12 @@ watch(
114
126
  />
115
127
  </template>
116
128
  <template #default>
117
- <RCBlockEditRenderer v-if="hasChildren" :blocks="children" />
129
+ <RCBlockEditor
130
+ v-model="localChildren"
131
+ :container-id="props.id"
132
+ @mutation="handleChildrenMutation"
133
+ @end="handleChildrenMutation"
134
+ />
118
135
  </template>
119
136
  </RCSection>
120
137
  </div>
@@ -83,7 +83,7 @@ const {
83
83
  } = pageEditorStyles();
84
84
  const { getTypeLabelKey } = usePageRegistry();
85
85
  const { t, locale } = useI18n();
86
- const { undo, redo, canUndo, canRedo, captureSnapshot, resetHistory } = usePageEditor(page);
86
+ const { undo, redo, canUndo, canRedo, captureSnapshot, resetHistory, pauseHistory, resumeHistory } = usePageEditor(page);
87
87
  const { confirm } = useConfirm();
88
88
  const handleViewPage = async () => {
89
89
  if (canUndo.value) {
@@ -347,7 +347,19 @@ const handleTreeNavigate = (slug) => {
347
347
  </div>
348
348
  </template>
349
349
  </UPageHeader>
350
- <RCBlockEditor ref="editor" v-model="page.blocks" @mutation="captureSnapshot" />
350
+ <RCBlockEditor
351
+ ref="editor"
352
+ v-model="page.blocks"
353
+ @start="pauseHistory"
354
+ @mutation="() => {
355
+ resumeHistory();
356
+ if (!editorRef?.isDragging) {
357
+ captureSnapshot();
358
+ } else {
359
+ console.log('[PageEditor] Skipping snapshot - editor is dragging');
360
+ }
361
+ }"
362
+ />
351
363
  <template v-if="useSurround">
352
364
  <div
353
365
  v-if="surroundStatus === 'pending'"
@@ -9,6 +9,7 @@ export declare function useBlockEditor(initialBlocks: Ref<Block[]>, { maxHistory
9
9
  moveBlock: (id: string, direction: -1 | 1) => void;
10
10
  duplicateBlock: (id: string) => void;
11
11
  insertBlock: (newBlockType: Block["type"], targetId?: string | null, position?: "before" | "after") => void;
12
+ relocateBlock: (blockId: string, targetContainerId: string | null, targetIndex: number) => void;
12
13
  undo: () => void;
13
14
  redo: () => void;
14
15
  canUndo: import("vue").ComputedRef<boolean>;
@@ -47,13 +47,7 @@ function createDefaultBlock(type) {
47
47
  type: "CalloutBlock",
48
48
  props: {
49
49
  variant: "info",
50
- children: [
51
- {
52
- id: uuidv7(),
53
- type: "ParagraphBlock",
54
- props: { text: [{ type: "text", id: uuidv7(), props: { content: "Callout content" } }] }
55
- }
56
- ]
50
+ children: []
57
51
  }
58
52
  };
59
53
  case "ImageBlock":
@@ -125,23 +119,24 @@ export function useBlockEditor(initialBlocks, { maxHistorySize = 100, onMutation
125
119
  const canUndo = computed(() => history.value.length > 0);
126
120
  const canRedo = computed(() => future.value.length > 0);
127
121
  const updateBlockProps = (id, newProps) => {
122
+ console.log("[useBlockEditor] updateBlockProps called for ID:", id, "Keys:", Object.keys(newProps));
123
+ if (newProps.children) {
124
+ console.log("[useBlockEditor] Updating children. Count:", newProps.children.length);
125
+ }
128
126
  executeMutation(() => {
129
127
  const loc = findBlockLocation(initialBlocks.value, id);
130
- if (!loc) return;
128
+ if (!loc) {
129
+ console.warn("[useBlockEditor] Block not found for update:", id);
130
+ return;
131
+ }
131
132
  const oldBlock = loc.parentArray[loc.index];
132
133
  if (!oldBlock) return;
133
- const newPropsObject = {
134
- ...oldBlock.props,
135
- ...newProps
136
- };
137
- const newBlock = {
134
+ Object.assign(oldBlock.props, newProps);
135
+ console.log("[useBlockEditor] Block props updated in place:", {
138
136
  id: oldBlock.id,
139
- // Explicitly carry over the required BaseBlock properties
140
137
  type: oldBlock.type,
141
- // Explicitly carry over the required BaseBlock properties
142
- props: newPropsObject
143
- };
144
- loc.parentArray.splice(loc.index, 1, newBlock);
138
+ childrenCount: oldBlock.props.children?.length
139
+ });
145
140
  });
146
141
  };
147
142
  const removeBlock = (id) => {
@@ -194,6 +189,49 @@ export function useBlockEditor(initialBlocks, { maxHistorySize = 100, onMutation
194
189
  committedBlocks.value = committedSnapshot;
195
190
  return committedSnapshot;
196
191
  };
192
+ const relocateBlock = (blockId, targetContainerId, targetIndex) => {
193
+ console.log("[useBlockEditor] relocateBlock called:", { blockId, targetContainerId, targetIndex });
194
+ executeMutation(() => {
195
+ const sourceLoc = findBlockLocation(initialBlocks.value, blockId);
196
+ if (!sourceLoc) {
197
+ console.warn("[useBlockEditor] Block not found for relocation:", blockId);
198
+ return;
199
+ }
200
+ const block = sourceLoc.parentArray[sourceLoc.index];
201
+ if (!block) {
202
+ console.warn("[useBlockEditor] Block is undefined at location");
203
+ return;
204
+ }
205
+ sourceLoc.parentArray.splice(sourceLoc.index, 1);
206
+ console.log("[useBlockEditor] Block removed from source, now inserting at target");
207
+ let targetArray;
208
+ if (targetContainerId === null) {
209
+ targetArray = initialBlocks.value;
210
+ } else {
211
+ const targetLoc = findBlockLocation(initialBlocks.value, targetContainerId);
212
+ if (!targetLoc) {
213
+ console.warn("[useBlockEditor] Target container not found:", targetContainerId);
214
+ sourceLoc.parentArray.splice(sourceLoc.index, 0, block);
215
+ return;
216
+ }
217
+ const targetBlock = targetLoc.parentArray[targetLoc.index];
218
+ if (!targetBlock || !("children" in targetBlock.props)) {
219
+ console.warn("[useBlockEditor] Target block does not have children prop");
220
+ sourceLoc.parentArray.splice(sourceLoc.index, 0, block);
221
+ return;
222
+ }
223
+ targetArray = targetBlock.props.children;
224
+ }
225
+ const safeIndex = Math.max(0, Math.min(targetIndex, targetArray.length));
226
+ targetArray.splice(safeIndex, 0, block);
227
+ console.log("[useBlockEditor] Block relocated successfully:", {
228
+ blockId,
229
+ targetContainerId,
230
+ targetIndex: safeIndex,
231
+ newTargetLength: targetArray.length
232
+ });
233
+ });
234
+ };
197
235
  return {
198
236
  // Mutations
199
237
  updateBlockProps,
@@ -201,6 +239,7 @@ export function useBlockEditor(initialBlocks, { maxHistorySize = 100, onMutation
201
239
  moveBlock,
202
240
  duplicateBlock,
203
241
  insertBlock,
242
+ relocateBlock,
204
243
  // History
205
244
  undo,
206
245
  redo,
@@ -47,13 +47,7 @@ function createDefaultBlock(type) {
47
47
  type: "CalloutBlock",
48
48
  props: {
49
49
  variant: "info",
50
- children: [
51
- {
52
- id: uuidv7(),
53
- type: "ParagraphBlock",
54
- props: { text: [{ type: "text", id: uuidv7(), props: { content: "Callout content" } }] }
55
- }
56
- ]
50
+ children: []
57
51
  }
58
52
  };
59
53
  case "ImageBlock":
@@ -125,23 +119,24 @@ export function useBlockEditor(initialBlocks, { maxHistorySize = 100, onMutation
125
119
  const canUndo = computed(() => history.value.length > 0);
126
120
  const canRedo = computed(() => future.value.length > 0);
127
121
  const updateBlockProps = (id, newProps) => {
122
+ console.log("[useBlockEditor] updateBlockProps called for ID:", id, "Keys:", Object.keys(newProps));
123
+ if (newProps.children) {
124
+ console.log("[useBlockEditor] Updating children. Count:", newProps.children.length);
125
+ }
128
126
  executeMutation(() => {
129
127
  const loc = findBlockLocation(initialBlocks.value, id);
130
- if (!loc) return;
128
+ if (!loc) {
129
+ console.warn("[useBlockEditor] Block not found for update:", id);
130
+ return;
131
+ }
131
132
  const oldBlock = loc.parentArray[loc.index];
132
133
  if (!oldBlock) return;
133
- const newPropsObject = {
134
- ...oldBlock.props,
135
- ...newProps
136
- };
137
- const newBlock = {
134
+ Object.assign(oldBlock.props, newProps);
135
+ console.log("[useBlockEditor] Block props updated in place:", {
138
136
  id: oldBlock.id,
139
- // Explicitly carry over the required BaseBlock properties
140
137
  type: oldBlock.type,
141
- // Explicitly carry over the required BaseBlock properties
142
- props: newPropsObject
143
- };
144
- loc.parentArray.splice(loc.index, 1, newBlock);
138
+ childrenCount: oldBlock.props.children?.length
139
+ });
145
140
  });
146
141
  };
147
142
  const removeBlock = (id) => {
@@ -194,6 +189,49 @@ export function useBlockEditor(initialBlocks, { maxHistorySize = 100, onMutation
194
189
  committedBlocks.value = committedSnapshot;
195
190
  return committedSnapshot;
196
191
  };
192
+ const relocateBlock = (blockId, targetContainerId, targetIndex) => {
193
+ console.log("[useBlockEditor] relocateBlock called:", { blockId, targetContainerId, targetIndex });
194
+ executeMutation(() => {
195
+ const sourceLoc = findBlockLocation(initialBlocks.value, blockId);
196
+ if (!sourceLoc) {
197
+ console.warn("[useBlockEditor] Block not found for relocation:", blockId);
198
+ return;
199
+ }
200
+ const block = sourceLoc.parentArray[sourceLoc.index];
201
+ if (!block) {
202
+ console.warn("[useBlockEditor] Block is undefined at location");
203
+ return;
204
+ }
205
+ sourceLoc.parentArray.splice(sourceLoc.index, 1);
206
+ console.log("[useBlockEditor] Block removed from source, now inserting at target");
207
+ let targetArray;
208
+ if (targetContainerId === null) {
209
+ targetArray = initialBlocks.value;
210
+ } else {
211
+ const targetLoc = findBlockLocation(initialBlocks.value, targetContainerId);
212
+ if (!targetLoc) {
213
+ console.warn("[useBlockEditor] Target container not found:", targetContainerId);
214
+ sourceLoc.parentArray.splice(sourceLoc.index, 0, block);
215
+ return;
216
+ }
217
+ const targetBlock = targetLoc.parentArray[targetLoc.index];
218
+ if (!targetBlock || !("children" in targetBlock.props)) {
219
+ console.warn("[useBlockEditor] Target block does not have children prop");
220
+ sourceLoc.parentArray.splice(sourceLoc.index, 0, block);
221
+ return;
222
+ }
223
+ targetArray = targetBlock.props.children;
224
+ }
225
+ const safeIndex = Math.max(0, Math.min(targetIndex, targetArray.length));
226
+ targetArray.splice(safeIndex, 0, block);
227
+ console.log("[useBlockEditor] Block relocated successfully:", {
228
+ blockId,
229
+ targetContainerId,
230
+ targetIndex: safeIndex,
231
+ newTargetLength: targetArray.length
232
+ });
233
+ });
234
+ };
197
235
  return {
198
236
  // Mutations
199
237
  updateBlockProps,
@@ -201,6 +239,7 @@ export function useBlockEditor(initialBlocks, { maxHistorySize = 100, onMutation
201
239
  moveBlock,
202
240
  duplicateBlock,
203
241
  insertBlock,
242
+ relocateBlock,
204
243
  // History
205
244
  undo,
206
245
  redo,
@@ -8,4 +8,6 @@ export declare function usePageEditor(page: Ref<Page>, maxHistorySize?: number):
8
8
  save: () => any;
9
9
  captureSnapshot: () => void;
10
10
  resetHistory: () => void;
11
+ pauseHistory: () => void;
12
+ resumeHistory: () => void;
11
13
  };
@@ -40,9 +40,22 @@ export function usePageEditor(page, maxHistorySize = 100) {
40
40
  const save = () => {
41
41
  return JSON.parse(JSON.stringify(page.value));
42
42
  };
43
+ let isPaused = false;
44
+ const pauseHistory = () => {
45
+ console.log("[usePageEditor] History paused");
46
+ isPaused = true;
47
+ };
48
+ const resumeHistory = () => {
49
+ console.log("[usePageEditor] History resumed");
50
+ isPaused = false;
51
+ };
43
52
  watch(
44
53
  [() => page.value.properties, () => page.value.title, () => page.value.blocks],
45
54
  () => {
55
+ if (isPaused) {
56
+ console.log("[usePageEditor] Skipping auto-snapshot - history is paused");
57
+ return;
58
+ }
46
59
  captureSnapshot();
47
60
  },
48
61
  { deep: true }
@@ -59,6 +72,8 @@ export function usePageEditor(page, maxHistorySize = 100) {
59
72
  canRedo,
60
73
  save,
61
74
  captureSnapshot,
62
- resetHistory
75
+ resetHistory,
76
+ pauseHistory,
77
+ resumeHistory
63
78
  };
64
79
  }
@@ -40,9 +40,22 @@ export function usePageEditor(page, maxHistorySize = 100) {
40
40
  const save = () => {
41
41
  return JSON.parse(JSON.stringify(page.value));
42
42
  };
43
+ let isPaused = false;
44
+ const pauseHistory = () => {
45
+ console.log("[usePageEditor] History paused");
46
+ isPaused = true;
47
+ };
48
+ const resumeHistory = () => {
49
+ console.log("[usePageEditor] History resumed");
50
+ isPaused = false;
51
+ };
43
52
  watch(
44
53
  [() => page.value.properties, () => page.value.title, () => page.value.blocks],
45
54
  () => {
55
+ if (isPaused) {
56
+ console.log("[usePageEditor] Skipping auto-snapshot - history is paused");
57
+ return;
58
+ }
46
59
  captureSnapshot();
47
60
  },
48
61
  { deep: true }
@@ -59,6 +72,8 @@ export function usePageEditor(page, maxHistorySize = 100) {
59
72
  canRedo,
60
73
  save,
61
74
  captureSnapshot,
62
- resetHistory
75
+ resetHistory,
76
+ pauseHistory,
77
+ resumeHistory
63
78
  };
64
79
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rimelight-components",
3
- "version": "2.1.93",
3
+ "version": "2.1.95",
4
4
  "description": "A component library by Rimelight Entertainment.",
5
5
  "keywords": [
6
6
  "nuxt",
@@ -74,7 +74,8 @@
74
74
  "defu": "^6.1.4",
75
75
  "tailwind-merge": "^3.4.0",
76
76
  "tailwind-variants": "^3.2.2",
77
- "uuid": "^13.0.0"
77
+ "uuid": "^13.0.0",
78
+ "vuedraggable": "^4.1.0"
78
79
  },
79
80
  "devDependencies": {
80
81
  "@commitlint/cli": "^20.4.1",