adminforth 2.23.0 → 2.25.0-next.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.
@@ -1,124 +1,188 @@
1
1
  <template>
2
- <div
3
- v-if="$slots.trigger"
4
- @click="modal?.show()" class="inline-flex items-center cursor-pointer"
2
+ <Modal
3
+ ref="modalRef"
4
+ v-bind="$attrs"
5
+ :closeByClickOutside="clickToCloseOutside || closeByClickOutside"
6
+ :closeByEsc="closeByEsc || closable"
7
+ :beforeCloseFunction="beforeCloseFunction"
8
+ :beforeOpenFunction="beforeOpenFunction"
9
+ :askForCloseConfirmation="askForCloseConfirmation"
10
+ :closeConfirmationText="closeConfirmationText"
11
+ :removeFromDomOnClose="removeFromDomOnClose"
5
12
  >
6
- <slot name="trigger"></slot>
7
- </div>
8
- <Teleport to="body">
9
- <div ref="modalEl" tabindex="-1" aria-hidden="true" class="[scrollbar-gutter:stable] hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-full max-h-full">
10
- <div v-bind="$attrs" class="relative p-4 max-w-2xl max-h-full" :class="($attrs.class as string)?.includes('w-') ? '' : 'w-full'">
11
- <!-- Modal content -->
12
- <div class="relative bg-lightDialogBackgorund rounded-lg shadow-sm dark:bg-darkDialogBackgorund">
13
- <!-- Modal header -->
14
- <div
15
- v-if="header"
16
- class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-darkDialogBreakLine border-lightDialogBreakLine"
17
- >
18
- <h3 class="text-xl font-semibold text-lightDialogHeaderText dark:text-darkDialogHeaderText">
19
- {{ header }}
20
- </h3>
21
- <button
22
- v-if="headerCloseButton"
23
- type="button"
24
- class="text-lightDialogCloseButton bg-transparent hover:bg-lightDialogCloseButtonHoverBackground hover:text-lightDialogCloseButtonHover rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:text-darkDialogCloseButton dark:hover:bg-darkDialogCloseButtonHoverBackground dark:hover:text-darkDialogCloseButtonHover"
25
- @click="tryToHideModal"
26
- >
27
- <svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
28
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
29
- </svg>
30
- <span class="sr-only">Close modal</span>
31
- </button>
32
- </div>
33
- <!-- Modal body -->
34
- <div class="p-4 md:p-5 space-y-4 text-lightDialogBodyText dark:text-darkDialogBodyText">
35
- <slot></slot>
36
- </div>
37
- <!-- Modal footer -->
38
- <div
39
- v-if="buttons.length"
40
- class="flex items-center p-4 md:p-5 border-t border-lightDialogBreakLine rounded-b dark:border-darkDialogBreakLine"
41
- >
42
- <Button
43
- v-for="(button, buttonIndex) in buttons"
44
- :key="buttonIndex"
45
- v-bind="button.options"
46
- :class="{ 'ms-3': buttonIndex > 0 }"
47
- @click="button.onclick(modal)"
48
- >
49
- {{ button.label }}
50
- </Button>
51
- </div>
52
- </div>
53
- </div>
54
- <div>
55
- <!-- Confirmation Modal -->
56
- <div
57
- v-if="showConfirmationOnClose"
58
- class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-[60]"
59
- >
60
- <div class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-lg max-w-sm w-full">
61
- <h2 class="text-lg font-semibold mb-4 text-lightDialogHeaderText dark:text-darkDialogHeaderText">Confirm Close</h2>
62
- <p class="mb-6 text-lightDialogBodyText dark:text-darkDialogBodyText">{{ props.closeConfirmationText }}</p>
63
- <div class="flex justify-end">
64
- <Button
65
- class="me-3 !bg-gray-50 dark:!bg-gray-700 !text-lightDialogBodyText dark:!text-darkDialogBodyText hover:!bg-gray-100 dark:hover:!bg-gray-600 !border-gray-200 dark:!border-gray-600"
66
- @click="showConfirmationOnClose = false"
67
- >
68
- Cancel
69
- </Button>
70
- <Button
71
- @click="
72
- showConfirmationOnClose = false;
73
- modal?.hide();
74
- "
75
- >
76
- Confirm
77
- </Button>
78
- </div>
79
- </div>
80
- </div>
81
- </div>
13
+ <template v-if="$slots.trigger" #trigger>
14
+ <slot name="trigger"></slot>
15
+ </template>
16
+
17
+ <!-- Modal header -->
18
+ <div
19
+ v-if="header"
20
+ class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-darkDialogBreakLine border-lightDialogBreakLine"
21
+ >
22
+ <h3 class="text-xl font-semibold text-lightDialogHeaderText dark:text-darkDialogHeaderText">
23
+ {{ header }}
24
+ </h3>
25
+ <button
26
+ v-if="headerCloseButton"
27
+ type="button"
28
+ class="text-lightDialogCloseButton bg-transparent hover:bg-lightDialogCloseButtonHoverBackground hover:text-lightDialogCloseButtonHover rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:text-darkDialogCloseButton dark:hover:bg-darkDialogCloseButtonHoverBackground dark:hover:text-darkDialogCloseButtonHover"
29
+ @click="tryToHideModal"
30
+ >
31
+ <svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
32
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
33
+ </svg>
34
+ <span class="sr-only">{{ t('Close Modal') }}</span>
35
+ </button>
36
+ </div>
37
+ <!-- Modal body -->
38
+ <div class="p-4 md:p-5 text-lightDialogBodyText dark:text-darkDialogBodyText">
39
+ <slot></slot>
40
+ </div>
41
+ <!-- Modal footer -->
42
+ <div
43
+ v-if="buttons.length"
44
+ class="flex items-center p-4 md:p-5 border-t border-lightDialogBreakLine rounded-b dark:border-darkDialogBreakLine"
45
+ >
46
+ <Button
47
+ v-for="(button, buttonIndex) in buttons"
48
+ :key="buttonIndex"
49
+ v-bind="button.options"
50
+ :class="{ 'ms-3': buttonIndex > 0 }"
51
+ @click="button.onclick(dialog)"
52
+ >
53
+ {{ button.label }}
54
+ </Button>
82
55
  </div>
83
- </Teleport>
56
+ </Modal>
84
57
  </template>
85
58
 
86
59
  <script setup lang="ts">
87
60
  import Button from "./Button.vue";
88
- import { ref, onMounted, nextTick, onUnmounted, computed, type Ref } from 'vue';
89
- import { Modal } from 'flowbite';
61
+ import { ref, computed, type Ref } from 'vue';
62
+ import { Modal } from '@/afcl'
63
+ import { useI18n } from "vue-i18n";
64
+
65
+ const { t } = useI18n();
66
+
67
+
68
+ interface IDialogInsideButtonClickHandler {
69
+ hide: () => void
70
+ }
90
71
 
91
- const modalEl = ref(null);
92
- const modal: Ref<Modal|null> = ref(null);
93
72
 
94
73
  interface DialogButton {
95
74
  label: string
96
- onclick: (dialog: any) => void
75
+ onclick: (dialog: IDialogInsideButtonClickHandler) => void
97
76
  options?: Record<string, any>
98
77
  }
99
78
 
79
+
100
80
  interface DialogProps {
81
+ /**
82
+ * The header text to display in the dialog. If not provided, no header will be displayed.
83
+ */
101
84
  header?: string
85
+
86
+ /**
87
+ * If true, a close button will be displayed in the dialog header. Default is true.
88
+ */
102
89
  headerCloseButton?: boolean
90
+
91
+ /**
92
+ * An array of buttons to display in the dialog footer.
93
+ */
103
94
  buttons?: DialogButton[]
95
+
96
+ /**
97
+ * If true, clicking outside the dialog will close it. Default is true.
98
+ *
99
+ * @deprecated Use `closeByClickOutside` instead
100
+ */
104
101
  clickToCloseOutside?: boolean
102
+
103
+ /**
104
+ * If true, pressing the Esc key will close the dialog. Default is true.
105
+ */
106
+ closeByEsc?: boolean
107
+
108
+ /**
109
+ * If true, clicking outside the dialog will close it. Default is true.
110
+ */
111
+ closeByClickOutside?: boolean
112
+
113
+ /**
114
+ * Function that will be called before the dialog is closed.
115
+ */
105
116
  beforeCloseFunction?: (() => void | Promise<void>) | null
117
+
118
+ /**
119
+ * Function that will be called before the dialog is opened.
120
+ */
106
121
  beforeOpenFunction?: (() => void | Promise<void>) | null
122
+
123
+ /**
124
+ * Disables close on Ecs button
125
+ *
126
+ * @deprecated Use `closeByEsc` or instead
127
+ */
107
128
  closable?: boolean
129
+
130
+ /**
131
+ * If true, the dialog will ask for confirmation before closing. Default is false.
132
+ */
108
133
  askForCloseConfirmation?: boolean
134
+
135
+ /**
136
+ * The text to display in the close confirmation dialog. Default is "Are you sure you want to close this dialog?".
137
+ */
109
138
  closeConfirmationText?: string
139
+
140
+ /**
141
+ * If true, the dialog will be removed from the DOM when closed. Default is false.
142
+ */
143
+ removeFromDomOnClose?: boolean
110
144
  }
111
145
 
146
+
147
+
148
+
149
+
150
+
151
+ /********** for the backward compatibility ***************/
152
+ class Dialog implements IDialogInsideButtonClickHandler {
153
+ hide: () => void
154
+ constructor( hide: () => void ) {
155
+ this.hide = hide;
156
+ }
157
+ }
158
+ const dialog: Ref<Dialog> = ref(
159
+ new Dialog(
160
+ () => {
161
+ if (dialog.value) {
162
+ tryToHideModal();
163
+ }
164
+ }
165
+ )
166
+ );
167
+ /*************************************************************/
168
+
169
+
170
+
171
+ const modalRef = ref();
172
+
112
173
  const props = withDefaults(defineProps<DialogProps>(), {
113
174
  header: '',
114
175
  headerCloseButton: true,
115
176
  buttons: () => [],
116
- clickToCloseOutside: true,
177
+ clickToCloseOutside: false,
178
+ closeByEsc: true,
179
+ closeByClickOutside: true,
117
180
  beforeCloseFunction: null,
118
181
  beforeOpenFunction: null,
119
- closable: true,
182
+ closable: false,
120
183
  askForCloseConfirmation: false,
121
184
  closeConfirmationText: 'Are you sure you want to close this dialog?',
185
+ removeFromDomOnClose: false,
122
186
  })
123
187
 
124
188
  const buttons = computed<DialogButton[]>(() => {
@@ -129,51 +193,20 @@ const buttons = computed<DialogButton[]>(() => {
129
193
  {
130
194
  label: 'Close',
131
195
  onclick: (dialog: any) => {
132
- if (!props.askForCloseConfirmation) {
133
- dialog.hide();
134
- } else {
135
- showConfirmationOnClose.value = true;
136
- }
196
+ tryToHideModal();
137
197
  },
138
198
  options: {}
139
199
  }
140
200
  ];
141
201
  });
142
202
 
143
- const showConfirmationOnClose = ref(false);
144
- onMounted(async () => {
145
- //await one tick when all is mounted
146
- await nextTick();
147
- modal.value = new Modal(
148
- modalEl.value,
149
- {
150
- closable: props.closable,
151
- backdrop: props.clickToCloseOutside ? 'dynamic' : 'static',
152
- onHide: async () => {
153
- if (props.beforeCloseFunction) {
154
- await props.beforeCloseFunction();
155
- }
156
- },
157
- onShow: async () => {
158
- if (props.beforeOpenFunction) {
159
- await props.beforeOpenFunction();
160
- }
161
- },
162
- }
163
- );
164
- })
165
-
166
- onUnmounted(() => {
167
- //destroy tooltip
168
- modal.value?.destroy();
169
- })
170
203
 
171
204
  function open() {
172
- modal.value?.show();
205
+ modalRef.value.open();
173
206
  }
174
207
 
175
208
  function close() {
176
- modal.value?.hide();
209
+ modalRef.value.hide();
177
210
  }
178
211
 
179
212
  defineExpose({
@@ -183,11 +216,7 @@ defineExpose({
183
216
  })
184
217
 
185
218
  function tryToHideModal() {
186
- if (!props.askForCloseConfirmation ) {
187
- modal.value?.hide();
188
- } else {
189
- showConfirmationOnClose.value = true;
190
- }
219
+ modalRef.value?.tryToHideModal();
191
220
  }
192
221
 
193
222
 
@@ -0,0 +1,137 @@
1
+ <template>
2
+ <div
3
+ v-if="$slots.trigger"
4
+ @click="toggleModal()" class="inline-flex items-center cursor-pointer w-full"
5
+ >
6
+ <slot name="trigger"></slot>
7
+ </div>
8
+ <Teleport to="body">
9
+ <div v-show="isModalOpen" v-if="!removeFromDom" @click="backdropClick" class="bg-black/50 overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full h-full md:inset-0 h-1rem max-h-full flex" >
10
+ <!-- Modal content -->
11
+ <div v-bind="$attrs" class="relative bg-lightDialogBackgorund rounded-lg shadow-sm dark:bg-darkDialogBackgorund">
12
+
13
+ <!-- Modal body -->
14
+ <div class="text-lightDialogBodyText dark:text-darkDialogBodyText">
15
+ <slot ></slot>
16
+ </div>
17
+
18
+ <!-- Confirmation Modal -->
19
+ <div
20
+ v-if="showConfirmationOnClose"
21
+ class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-[60]"
22
+ >
23
+ <div class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-lg max-w-sm w-full">
24
+ <h2 class="text-lg font-semibold mb-4 text-lightDialogHeaderText dark:text-darkDialogHeaderText">Confirm Close</h2>
25
+ <p class="mb-6 text-lightDialogBodyText dark:text-darkDialogBodyText">{{ props.closeConfirmationText }}</p>
26
+ <div class="flex justify-end">
27
+ <Button
28
+ class="me-3 !bg-gray-50 dark:!bg-gray-700 !text-lightDialogBodyText dark:!text-darkDialogBodyText hover:!bg-gray-100 dark:hover:!bg-gray-600 !border-gray-200 dark:!border-gray-600"
29
+ @click="showConfirmationOnClose = false"
30
+ >
31
+ Cancel
32
+ </Button>
33
+ <Button
34
+ @click="
35
+ showConfirmationOnClose = false;
36
+ close();
37
+ "
38
+ >
39
+ Confirm
40
+ </Button>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </Teleport>
47
+ </template>
48
+
49
+ <script setup lang="ts">
50
+ import Button from "./Button.vue";
51
+ import { ref, onMounted, nextTick, onUnmounted, computed, type Ref } from 'vue';
52
+
53
+ const isModalOpen = ref(false);
54
+
55
+ const removeFromDom = computed(() => {
56
+ return props.removeFromDomOnClose && !isModalOpen.value;
57
+ })
58
+
59
+ interface DialogProps {
60
+ closeByClickOutside?: boolean
61
+ closeByEsc?: boolean
62
+ beforeCloseFunction?: (() => void | Promise<void>) | null
63
+ beforeOpenFunction?: (() => void | Promise<void>) | null
64
+ askForCloseConfirmation?: boolean
65
+ closeConfirmationText?: string
66
+ removeFromDomOnClose?: boolean
67
+ }
68
+
69
+ const props = withDefaults(defineProps<DialogProps>(), {
70
+ closeByClickOutside: true,
71
+ closeByEsc: true,
72
+ beforeCloseFunction: null,
73
+ beforeOpenFunction: null,
74
+ askForCloseConfirmation: false,
75
+ closeConfirmationText: 'Are you sure you want to close this dialog?',
76
+ removeFromDomOnClose: false,
77
+ })
78
+
79
+ const showConfirmationOnClose = ref(false);
80
+
81
+
82
+ async function open() {
83
+ await props.beforeOpenFunction?.();
84
+ isModalOpen.value = true;
85
+ }
86
+
87
+ async function close() {
88
+ await props.beforeCloseFunction?.();
89
+ isModalOpen.value = false;
90
+ }
91
+
92
+ defineExpose({
93
+ open: open,
94
+ close: close,
95
+ tryToHideModal: tryToHideModal,
96
+ })
97
+
98
+ function tryToHideModal() {
99
+ if (!props.askForCloseConfirmation ) {
100
+ close();
101
+ } else {
102
+ showConfirmationOnClose.value = true;
103
+ }
104
+ }
105
+
106
+ function toggleModal() {
107
+ if (isModalOpen.value) {
108
+ tryToHideModal();
109
+ } else {
110
+ open();
111
+ }
112
+ }
113
+
114
+ function onEsc(event: KeyboardEvent) {
115
+ if (event.key === 'Escape') {
116
+ if (props.closeByEsc) {
117
+ tryToHideModal();
118
+ }
119
+ }
120
+ }
121
+
122
+ onMounted(() => {
123
+ document.addEventListener('keydown', onEsc)
124
+ })
125
+
126
+ onUnmounted(() => {
127
+ document.removeEventListener('keydown', onEsc)
128
+ })
129
+
130
+
131
+ function backdropClick(e: MouseEvent) {
132
+ if (props.closeByClickOutside && e.target === e.currentTarget) {
133
+ tryToHideModal();
134
+ }
135
+ }
136
+
137
+ </script>
@@ -1,14 +1,17 @@
1
1
  <template>
2
- <div class="relative mt-4 lg:mt-10 w-full max-w-[700px] bg-lightProgressBarUnfilledColor rounded-full h-2.5 dark:bg-darkProgressBarUnfilledColor">
3
- <span class="absolute -top-6 left-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ leftLabel }}</span>
4
- <span class="absolute -top-6 right-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ rightLabel }}</span>
2
+ <div class="relative w-full bg-lightProgressBarUnfilledColor rounded-full h-2.5 dark:bg-darkProgressBarUnfilledColor" :class="props.height ? `h-${props.height}` : ''">
3
+ <span v-if="leftLabel" class="absolute -top-6 left-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ leftLabel }}</span>
4
+ <span v-if="rightLabel" class="absolute -top-6 right-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ rightLabel }}</span>
5
5
  <div
6
6
  class="bg-lightProgressBarFilledColor dark:bg-darkProgressBarFilledColor h-2.5 rounded-full transition-all duration-300 ease-in-out"
7
+ :class="{ 'progress-bar': showAnimation, [`h-${props.height}`]: props.height }"
7
8
  :style="{ width: `${percentage}%` }"
8
9
  ></div>
9
- <span v-if="showValues" class="absolute top-4 left-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ formatValue(minValue) }}</span>
10
- <span v-if="showProgress" class="absolute top-4 right-1/2 translate-x-1/2 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ progressText }}</span>
11
- <span v-if="showValues" class="absolute top-4 right-0 text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ formatValue(maxValue) }}</span>
10
+ <div v-if="showValues || showProgress" class="flex justify-between mt-2">
11
+ <span v-if="showValues" class="text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ formatValue(minValue) }}</span>
12
+ <span v-if="showProgress" class="text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ progressText }}</span>
13
+ <span v-if="showValues" class="text-sm text-lightProgressBarText dark:text-darkProgressBarText">{{ formatValue(maxValue) }}</span>
14
+ </div>
12
15
  </div>
13
16
  </template>
14
17
 
@@ -26,6 +29,8 @@ interface Props {
26
29
  showLabels?: boolean
27
30
  showValues?: boolean
28
31
  showProgress?: boolean
32
+ showAnimation?: boolean
33
+ height?: number
29
34
  }
30
35
 
31
36
  const props = withDefaults(defineProps<Props>(), {
@@ -53,3 +58,34 @@ const formatValue = (value: number): string => {
53
58
  return formatter(value)
54
59
  }
55
60
  </script>
61
+
62
+
63
+ <style scoped>
64
+ .progress-bar {
65
+ position: relative;
66
+ overflow: hidden;
67
+ }
68
+
69
+ .progress-bar::after {
70
+ content: "";
71
+ position: absolute;
72
+ inset: 0;
73
+ width: 100%;
74
+
75
+ background: linear-gradient(
76
+ -45deg,
77
+ transparent 35%,
78
+ rgba(255,255,255,0.4),
79
+ transparent 65%
80
+ );
81
+
82
+ transform: translateX(-100%);
83
+ animation: progress-slide 2s linear infinite;
84
+ }
85
+
86
+ @keyframes progress-slide {
87
+ 100% {
88
+ transform: translateX(100%);
89
+ }
90
+ }
91
+ </style>
@@ -0,0 +1,136 @@
1
+ <template>
2
+ <div class="afcl-treemap -mb-2" ref="chart"></div>
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import ApexCharts, { type ApexOptions } from 'apexcharts';
7
+ import { ref, type Ref, watch, computed, onUnmounted } from 'vue';
8
+
9
+ const chart: Ref<HTMLDivElement | null> = ref(null);
10
+
11
+ const props = defineProps<{
12
+ data: {
13
+ x: string,
14
+ [key: string]: any,
15
+ }[],
16
+ series: {
17
+ name: string,
18
+ fieldName: string,
19
+ }[],
20
+ options?: ApexOptions,
21
+ }>();
22
+
23
+ const optionsBase: ApexOptions = {
24
+ chart: {
25
+ height: 350,
26
+ type: 'treemap',
27
+ fontFamily: 'Inter, sans-serif',
28
+ toolbar: {
29
+ show: false,
30
+ },
31
+ },
32
+ legend: {
33
+ show: false,
34
+ },
35
+ dataLabels: {
36
+ enabled: true,
37
+ style: {
38
+ fontFamily: 'Inter, sans-serif',
39
+ colors: ['#FFFFFF'],
40
+ },
41
+ },
42
+ plotOptions: {
43
+ treemap: {
44
+ distributed: true,
45
+ enableShades: false,
46
+ },
47
+ },
48
+ };
49
+
50
+ const options = computed(() => {
51
+ if (props.data?.length > 0) {
52
+ props.series.forEach((s) => {
53
+ if (props.data[0][s.fieldName] === undefined) {
54
+ throw new Error(
55
+ `Field ${s.fieldName} not found even in first data point ${JSON.stringify(props.data[0])}, something is wrong`,
56
+ );
57
+ }
58
+ });
59
+ }
60
+
61
+ const nextOptions: ApexOptions = {
62
+ ...optionsBase,
63
+ series: props.series.map((s) => ({
64
+ name: s.name,
65
+ data: (props.data ?? []).map((item: any) => {
66
+ const { x, y: _ignoredY, ...rest } = item ?? {};
67
+ return {
68
+ x,
69
+ y: item?.[s.fieldName],
70
+ ...rest,
71
+ };
72
+ }),
73
+ })),
74
+ };
75
+
76
+ function mergeOptions(target: any, source: any) {
77
+ if (!source) {
78
+ return;
79
+ }
80
+ for (const key in source) {
81
+ if (typeof source[key] === 'object' && !Array.isArray(source[key])) {
82
+ if (!target[key]) {
83
+ target[key] = {};
84
+ }
85
+ mergeOptions(target[key], source[key]);
86
+ } else {
87
+ target[key] = source[key];
88
+ }
89
+ }
90
+ }
91
+
92
+ mergeOptions(nextOptions, props.options);
93
+ return nextOptions;
94
+ });
95
+
96
+ let apexChart: ApexCharts | null = null;
97
+
98
+ watch(() => [options.value, chart.value], (value) => {
99
+ if (!value || !chart.value) {
100
+ return;
101
+ }
102
+
103
+ if (apexChart) {
104
+ apexChart.updateOptions(options.value);
105
+ } else {
106
+ apexChart = new ApexCharts(chart.value, options.value);
107
+ apexChart.render();
108
+ }
109
+ });
110
+
111
+ onUnmounted(() => {
112
+ if (apexChart) {
113
+ apexChart.destroy();
114
+ }
115
+ });
116
+ </script>
117
+
118
+ <style lang="scss">
119
+ :root {
120
+ --afcl-treemap-text: #FFFFFF;
121
+ }
122
+
123
+ [data-theme='dark'] {
124
+ --afcl-treemap-text: #FFFFFF;
125
+ }
126
+
127
+ .afcl-treemap {
128
+ .apexcharts-datalabel {
129
+ fill: var(--afcl-treemap-text);
130
+ }
131
+
132
+ .apexcharts-legend-text {
133
+ color: var(--afcl-treemap-text) !important;
134
+ }
135
+ }
136
+ </style>