nuxt-ui-elements-pro 0.1.10 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/module.d.mts +5 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +240 -1
  4. package/dist/runtime/components/EventCalendar.vue +2 -2
  5. package/dist/runtime/components/FeedbackWidget.d.vue.ts +118 -0
  6. package/dist/runtime/components/FeedbackWidget.vue +141 -0
  7. package/dist/runtime/components/FeedbackWidget.vue.d.ts +118 -0
  8. package/dist/runtime/components/GanttChart.d.vue.ts +138 -0
  9. package/dist/runtime/components/GanttChart.vue +206 -0
  10. package/dist/runtime/components/GanttChart.vue.d.ts +138 -0
  11. package/dist/runtime/components/event-calendar/EventCalendarTimeGrid.vue +23 -3
  12. package/dist/runtime/components/feedback-widget/FeedbackWidgetButton.d.vue.ts +22 -0
  13. package/dist/runtime/components/feedback-widget/FeedbackWidgetButton.vue +27 -0
  14. package/dist/runtime/components/feedback-widget/FeedbackWidgetButton.vue.d.ts +22 -0
  15. package/dist/runtime/components/feedback-widget/FeedbackWidgetPanel.d.vue.ts +42 -0
  16. package/dist/runtime/components/feedback-widget/FeedbackWidgetPanel.vue +288 -0
  17. package/dist/runtime/components/feedback-widget/FeedbackWidgetPanel.vue.d.ts +42 -0
  18. package/dist/runtime/components/gantt-chart/GanttChartDependencies.d.vue.ts +16 -0
  19. package/dist/runtime/components/gantt-chart/GanttChartDependencies.vue +70 -0
  20. package/dist/runtime/components/gantt-chart/GanttChartDependencies.vue.d.ts +16 -0
  21. package/dist/runtime/components/gantt-chart/GanttChartHeader.d.vue.ts +41 -0
  22. package/dist/runtime/components/gantt-chart/GanttChartHeader.vue +56 -0
  23. package/dist/runtime/components/gantt-chart/GanttChartHeader.vue.d.ts +41 -0
  24. package/dist/runtime/components/gantt-chart/GanttChartTimeline.d.vue.ts +34 -0
  25. package/dist/runtime/components/gantt-chart/GanttChartTimeline.vue +193 -0
  26. package/dist/runtime/components/gantt-chart/GanttChartTimeline.vue.d.ts +34 -0
  27. package/dist/runtime/composables/useEventCalendar.d.ts +2 -0
  28. package/dist/runtime/composables/useEventCalendar.js +8 -6
  29. package/dist/runtime/composables/useEventCalendarContext.d.ts +2 -7
  30. package/dist/runtime/composables/useEventCalendarContext.js +4 -11
  31. package/dist/runtime/composables/useFeedbackWidget.d.ts +46 -0
  32. package/dist/runtime/composables/useFeedbackWidget.js +137 -0
  33. package/dist/runtime/composables/useFeedbackWidgetContext.d.ts +3 -0
  34. package/dist/runtime/composables/useFeedbackWidgetContext.js +4 -0
  35. package/dist/runtime/composables/useGanttChart.d.ts +52 -0
  36. package/dist/runtime/composables/useGanttChart.js +224 -0
  37. package/dist/runtime/composables/useGanttChartContext.d.ts +3 -0
  38. package/dist/runtime/composables/useGanttChartContext.js +4 -0
  39. package/dist/runtime/composables/useGanttChartDragDrop.d.ts +28 -0
  40. package/dist/runtime/composables/useGanttChartDragDrop.js +68 -0
  41. package/dist/runtime/composables/useGanttChartKeyboard.d.ts +14 -0
  42. package/dist/runtime/composables/useGanttChartKeyboard.js +92 -0
  43. package/dist/runtime/composables/useGanttChartResize.d.ts +26 -0
  44. package/dist/runtime/composables/useGanttChartResize.js +89 -0
  45. package/dist/runtime/composables/useOrgChartContext.d.ts +2 -7
  46. package/dist/runtime/composables/useOrgChartContext.js +4 -11
  47. package/dist/runtime/index.d.ts +1 -0
  48. package/dist/runtime/index.js +1 -0
  49. package/dist/runtime/server/api/_feedback.post.d.ts +3 -0
  50. package/dist/runtime/server/api/_feedback.post.js +115 -0
  51. package/dist/runtime/server/nodemailer.d.ts +29 -0
  52. package/dist/runtime/server/utils/feedback-captcha.d.ts +1 -0
  53. package/dist/runtime/server/utils/feedback-captcha.js +27 -0
  54. package/dist/runtime/server/utils/feedback-rate-limit.d.ts +4 -0
  55. package/dist/runtime/server/utils/feedback-rate-limit.js +26 -0
  56. package/dist/runtime/types/event-calendar.d.ts +10 -4
  57. package/dist/runtime/types/feedback-widget.d.ts +110 -0
  58. package/dist/runtime/types/feedback-widget.js +0 -0
  59. package/dist/runtime/types/gantt-chart.d.ts +223 -0
  60. package/dist/runtime/types/gantt-chart.js +0 -0
  61. package/dist/runtime/types/index.d.ts +4 -0
  62. package/dist/runtime/types/index.js +4 -0
  63. package/dist/runtime/utils/createComponentContext.d.ts +15 -0
  64. package/dist/runtime/utils/createComponentContext.js +17 -0
  65. package/dist/runtime/utils/date.d.ts +10 -0
  66. package/dist/runtime/utils/date.js +9 -0
  67. package/dist/runtime/utils/event-calendar.d.ts +1 -9
  68. package/dist/runtime/utils/event-calendar.js +2 -9
  69. package/dist/runtime/utils/gantt-chart.d.ts +85 -0
  70. package/dist/runtime/utils/gantt-chart.js +549 -0
  71. package/dist/runtime/utils/recurrence.js +2 -1
  72. package/package.json +17 -8
@@ -0,0 +1,193 @@
1
+ <script setup>
2
+ import { useSlots } from "vue";
3
+ import { useGanttChartContext } from "../../composables/useGanttChartContext";
4
+ import GanttChartDependencies from "./GanttChartDependencies.vue";
5
+ const ctx = useGanttChartContext();
6
+ const slots = useSlots();
7
+ </script>
8
+
9
+ <template>
10
+ <div data-slot="timeline" :class="ctx.ui.value.timeline({ class: ctx.propUi.value?.timeline })" :style="{ width: `${ctx.layout.value.totalWidth}px` }">
11
+ <!-- Sticky header -->
12
+ <div data-slot="timelineHeader" :class="ctx.ui.value.timelineHeader({ class: ctx.propUi.value?.timelineHeader })">
13
+ <!-- Top row: header groups -->
14
+ <div data-slot="headerGroupRow" :class="ctx.ui.value.headerGroupRow({ class: ctx.propUi.value?.headerGroupRow })">
15
+ <div
16
+ v-for="(group, i) in ctx.layout.value.headerGroups"
17
+ :key="i"
18
+ data-slot="headerGroup"
19
+ :class="ctx.ui.value.headerGroup({ class: ctx.propUi.value?.headerGroup })"
20
+ :style="{ width: `${group.widthPx}px` }"
21
+ >
22
+ {{ group.label }}
23
+ </div>
24
+ </div>
25
+ <!-- Bottom row: columns -->
26
+ <div data-slot="headerColumnRow" :class="ctx.ui.value.headerColumnRow({ class: ctx.propUi.value?.headerColumnRow })">
27
+ <div
28
+ v-for="(col, i) in ctx.layout.value.columns"
29
+ :key="i"
30
+ data-slot="headerColumn"
31
+ :class="[
32
+ ctx.ui.value.headerColumn({ class: ctx.propUi.value?.headerColumn }),
33
+ col.isWeekend && ctx.ui.value.headerColumnWeekend({ class: ctx.propUi.value?.headerColumnWeekend }),
34
+ col.isToday && ctx.ui.value.headerColumnToday({ class: ctx.propUi.value?.headerColumnToday })
35
+ ]"
36
+ :style="{ width: `${col.widthPx}px` }"
37
+ >
38
+ {{ col.label }}
39
+ </div>
40
+ </div>
41
+ </div>
42
+
43
+ <!-- Grid body -->
44
+ <div data-slot="grid" :class="ctx.ui.value.grid({ class: ctx.propUi.value?.grid })" :style="{ height: `${ctx.layout.value.totalHeight}px` }">
45
+ <!-- Vertical grid lines -->
46
+ <div
47
+ v-for="(col, i) in ctx.layout.value.columns"
48
+ :key="`line-${i}`"
49
+ :class="[
50
+ ctx.ui.value.gridLine({ class: ctx.propUi.value?.gridLine }),
51
+ col.isWeekend && ctx.ui.value.weekendColumn({ class: ctx.propUi.value?.weekendColumn })
52
+ ]"
53
+ :style="{ left: `${col.leftPx}px`, width: `${col.widthPx}px`, height: '100%' }"
54
+ />
55
+
56
+ <!-- Row stripes -->
57
+ <div
58
+ v-for="(bar, i) in ctx.layout.value.taskBars"
59
+ :key="`row-${bar.task.id}`"
60
+ :class="[
61
+ ctx.ui.value.row({ class: ctx.propUi.value?.row }),
62
+ i % 2 === 0 && ctx.ui.value.rowEven({ class: ctx.propUi.value?.rowEven })
63
+ ]"
64
+ :style="{ top: `${i * ctx.rowHeight.value}px`, height: `${ctx.rowHeight.value}px` }"
65
+ />
66
+
67
+ <!-- Today marker -->
68
+ <div
69
+ v-if="ctx.layout.value.todayOffsetPx !== null"
70
+ data-slot="todayMarker"
71
+ :class="ctx.ui.value.todayMarker({ class: ctx.propUi.value?.todayMarker })"
72
+ :style="{ left: `${ctx.layout.value.todayOffsetPx}px`, height: '100%' }"
73
+ >
74
+ <slot name="today-marker" />
75
+ </div>
76
+
77
+ <!-- Dependency arrows (rendered before task bars so they appear behind them) -->
78
+ <GanttChartDependencies>
79
+ <template v-if="slots.dependency" #default="depProps">
80
+ <slot name="dependency" v-bind="depProps" />
81
+ </template>
82
+ </GanttChartDependencies>
83
+
84
+ <!-- Task bars -->
85
+ <template v-for="bar in ctx.layout.value.taskBars" :key="bar.task.id">
86
+ <!-- Milestone -->
87
+ <div
88
+ v-if="bar.isMilestone"
89
+ data-slot="milestone"
90
+ :class="ctx.ui.value.milestone({ class: ctx.propUi.value?.milestone })"
91
+ :style="{
92
+ left: `${bar.leftPx - 7}px`,
93
+ top: `${bar.rowIndex * ctx.rowHeight.value + (ctx.rowHeight.value - 14) / 2}px`,
94
+ paddingLeft: `${bar.depth * 16}px`
95
+ }"
96
+ :data-task-id="bar.task.id"
97
+ :data-focused="ctx.focusedTaskId.value === bar.task.id || void 0"
98
+ role="treeitem"
99
+ :aria-label="`Milestone: ${bar.task.title}`"
100
+ @click="ctx.handleTaskClick(bar.task, $event)"
101
+ >
102
+ <slot name="milestone" :task="bar.task.original" :bar="bar">
103
+ <div :class="ctx.ui.value.milestoneDiamond({ class: ctx.propUi.value?.milestoneDiamond })" :style="ctx.getTaskStyle(bar.task)" />
104
+ </slot>
105
+ </div>
106
+
107
+ <!-- Group bar -->
108
+ <div
109
+ v-else-if="bar.isGroup"
110
+ data-slot="groupBar"
111
+ :class="ctx.ui.value.groupBar({ class: ctx.propUi.value?.groupBar })"
112
+ :style="{
113
+ left: `${bar.leftPx}px`,
114
+ width: `${bar.widthPx}px`,
115
+ top: `${bar.rowIndex * ctx.rowHeight.value}px`,
116
+ height: `${ctx.rowHeight.value}px`,
117
+ paddingLeft: `${bar.depth * 16}px`
118
+ }"
119
+ :data-task-id="bar.task.id"
120
+ :data-focused="ctx.focusedTaskId.value === bar.task.id || void 0"
121
+ role="treeitem"
122
+ :aria-expanded="bar.isExpanded"
123
+ :aria-label="`${bar.task.title}, ${bar.childCount} subtasks`"
124
+ @click="ctx.handleTaskClick(bar.task, $event)"
125
+ >
126
+ <slot name="group-header" :task="bar.task.original" :bar="bar" :expanded="bar.isExpanded" :toggle="() => ctx.toggleExpand(bar.task.id)">
127
+ <button
128
+ :class="ctx.ui.value.groupToggle({ class: ctx.propUi.value?.groupToggle })"
129
+ :aria-label="bar.isExpanded ? 'Collapse group' : 'Expand group'"
130
+ @click.stop="ctx.toggleExpand(bar.task.id)"
131
+ >
132
+ <UIcon :name="bar.isExpanded ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'" class="size-3" />
133
+ </button>
134
+ <span :class="ctx.ui.value.groupTitle({ class: ctx.propUi.value?.groupTitle })">{{ bar.task.title }}</span>
135
+ <!-- Summary bar shape -->
136
+ <div :class="ctx.ui.value.groupBarShape({ class: ctx.propUi.value?.groupBarShape })" :style="ctx.getTaskStyle(bar.task)" />
137
+ <div :class="ctx.ui.value.groupBarEndCap({ class: ctx.propUi.value?.groupBarEndCap })" :style="{ left: '0', ...ctx.getTaskStyle(bar.task) }" />
138
+ <div :class="ctx.ui.value.groupBarEndCap({ class: ctx.propUi.value?.groupBarEndCap })" :style="{ right: '0', ...ctx.getTaskStyle(bar.task) }" />
139
+ </slot>
140
+ </div>
141
+
142
+ <!-- Regular task bar -->
143
+ <div
144
+ v-else
145
+ data-slot="taskBar"
146
+ :class="[
147
+ ctx.ui.value.taskBar({ class: ctx.propUi.value?.taskBar }),
148
+ ctx.draggedTaskId.value === bar.task.id && 'opacity-50'
149
+ ]"
150
+ :style="{
151
+ left: `${ctx.draggedTaskId.value === bar.task.id && ctx.dragPreview.value ? ctx.dragPreview.value.leftPx : ctx.resizingTaskId.value === bar.task.id && ctx.resizePreview.value ? ctx.resizePreview.value.leftPx : bar.leftPx}px`,
152
+ width: `${ctx.resizingTaskId.value === bar.task.id && ctx.resizePreview.value ? ctx.resizePreview.value.widthPx : bar.widthPx}px`,
153
+ top: `${bar.rowIndex * ctx.rowHeight.value + 4}px`,
154
+ height: `${ctx.rowHeight.value - 8}px`,
155
+ marginLeft: `${bar.depth * 16}px`
156
+ }"
157
+ :data-task-id="bar.task.id"
158
+ :data-focused="ctx.focusedTaskId.value === bar.task.id || void 0"
159
+ role="treeitem"
160
+ :aria-label="bar.task.title"
161
+ @pointerdown="ctx.editable.value && bar.task.draggable ? ctx.onDragStart(bar, $event) : void 0"
162
+ @click="ctx.handleTaskClick(bar.task, $event)"
163
+ >
164
+ <slot name="task-bar" :task="bar.task.original" :bar="bar">
165
+ <!-- Background -->
166
+ <div :class="ctx.ui.value.taskBarInner({ class: ctx.propUi.value?.taskBarInner })" :style="ctx.getTaskStyle(bar.task)" />
167
+ <!-- Progress fill -->
168
+ <div
169
+ v-if="bar.task.progress > 0"
170
+ :class="ctx.ui.value.taskBarProgress({ class: ctx.propUi.value?.taskBarProgress })"
171
+ :style="{ width: `${bar.task.progress}%`, ...ctx.getTaskStyle(bar.task) }"
172
+ />
173
+ <span :class="ctx.ui.value.taskBarTitle({ class: ctx.propUi.value?.taskBarTitle })">{{ bar.task.title }}</span>
174
+ </slot>
175
+ <!-- Left resize handle -->
176
+ <div
177
+ v-if="ctx.editable.value && bar.task.resizable"
178
+ :class="ctx.ui.value.resizeHandleLeft({ class: ctx.propUi.value?.resizeHandleLeft })"
179
+ :style="ctx.getTaskStyle(bar.task)"
180
+ @pointerdown.stop="ctx.onResizePointerDown(bar, 'start', $event)"
181
+ />
182
+ <!-- Right resize handle -->
183
+ <div
184
+ v-if="ctx.editable.value && bar.task.resizable"
185
+ :class="ctx.ui.value.resizeHandleRight({ class: ctx.propUi.value?.resizeHandleRight })"
186
+ :style="ctx.getTaskStyle(bar.task)"
187
+ @pointerdown.stop="ctx.onResizePointerDown(bar, 'end', $event)"
188
+ />
189
+ </div>
190
+ </template>
191
+ </div>
192
+ </div>
193
+ </template>
@@ -0,0 +1,34 @@
1
+ declare var __VLS_1: {}, __VLS_10: {
2
+ paths: import("../../types/gantt-chart.js").GanttDependencyPath[];
3
+ }, __VLS_12: {
4
+ task: import("../../types/gantt-chart.js").GanttTask;
5
+ bar: import("../../types/gantt-chart.js").GanttTaskBar;
6
+ }, __VLS_14: {
7
+ task: import("../../types/gantt-chart.js").GanttTask;
8
+ bar: import("../../types/gantt-chart.js").GanttTaskBar;
9
+ expanded: boolean;
10
+ toggle: () => void;
11
+ }, __VLS_21: {
12
+ task: import("../../types/gantt-chart.js").GanttTask;
13
+ bar: import("../../types/gantt-chart.js").GanttTaskBar;
14
+ };
15
+ type __VLS_Slots = {} & {
16
+ 'today-marker'?: (props: typeof __VLS_1) => any;
17
+ } & {
18
+ dependency?: (props: typeof __VLS_10) => any;
19
+ } & {
20
+ milestone?: (props: typeof __VLS_12) => any;
21
+ } & {
22
+ 'group-header'?: (props: typeof __VLS_14) => any;
23
+ } & {
24
+ 'task-bar'?: (props: typeof __VLS_21) => any;
25
+ };
26
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
27
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
28
+ declare const _default: typeof __VLS_export;
29
+ export default _default;
30
+ type __VLS_WithSlots<T, S> = T & {
31
+ new (): {
32
+ $slots: S;
33
+ };
34
+ };
@@ -23,11 +23,13 @@ export declare function useEventCalendar(options: UseEventCalendarOptions): {
23
23
  startHour: number;
24
24
  endHour: number;
25
25
  slotDuration: 15 | 30 | 60;
26
+ scrollTime: number;
26
27
  }>;
27
28
  dayConfig: import("vue").ComputedRef<{
28
29
  startHour: number;
29
30
  endHour: number;
30
31
  slotDuration: 15 | 30 | 60;
32
+ scrollTime: number;
31
33
  }>;
32
34
  monthWeeks: import("vue").ComputedRef<CalendarWeek[]>;
33
35
  monthWeekLayouts: import("vue").ComputedRef<MonthWeekLayout[]>;
@@ -23,14 +23,16 @@ export function useEventCalendar(options) {
23
23
  fixedWeeks: options.monthOptions()?.fixedWeeks ?? true
24
24
  }));
25
25
  const weekConfig = computed(() => ({
26
- startHour: options.weekOptions()?.startHour ?? 7,
27
- endHour: options.weekOptions()?.endHour ?? 22,
28
- slotDuration: options.weekOptions()?.slotDuration ?? 30
26
+ startHour: options.weekOptions()?.startHour ?? 0,
27
+ endHour: options.weekOptions()?.endHour ?? 24,
28
+ slotDuration: options.weekOptions()?.slotDuration ?? 30,
29
+ scrollTime: options.weekOptions()?.scrollTime ?? 7
29
30
  }));
30
31
  const dayConfig = computed(() => ({
31
- startHour: options.dayOptions()?.startHour ?? 7,
32
- endHour: options.dayOptions()?.endHour ?? 22,
33
- slotDuration: options.dayOptions()?.slotDuration ?? 30
32
+ startHour: options.dayOptions()?.startHour ?? 0,
33
+ endHour: options.dayOptions()?.endHour ?? 24,
34
+ slotDuration: options.dayOptions()?.slotDuration ?? 30,
35
+ scrollTime: options.dayOptions()?.scrollTime ?? 7
34
36
  }));
35
37
  const displayDate = computed(() => {
36
38
  const mv = options.modelValue();
@@ -1,8 +1,3 @@
1
- import type { InjectionKey } from "vue";
2
1
  import type { EventCalendarContext } from "../types/event-calendar.js";
3
- export declare const eventCalendarContextKey: InjectionKey<EventCalendarContext>;
4
- /**
5
- * Inject the EventCalendar context in a sub-component.
6
- * Throws if called outside an <UEEventCalendar> root.
7
- */
8
- export declare function useEventCalendarContext(): EventCalendarContext;
2
+ export declare const eventCalendarContextKey: import("vue").InjectionKey<EventCalendarContext>;
3
+ export declare const useEventCalendarContext: () => EventCalendarContext;
@@ -1,11 +1,4 @@
1
- import { inject } from "vue";
2
- export const eventCalendarContextKey = Symbol("nuxt-ui-elements-pro.event-calendar");
3
- export function useEventCalendarContext() {
4
- const ctx = inject(eventCalendarContextKey);
5
- if (!ctx) {
6
- throw new Error(
7
- "[EventCalendar] useEventCalendarContext() was called outside of an <UEEventCalendar> root. Sub-components (UEEventCalendarHeader, UEEventCalendarMonthView, UEEventCalendarTimeGrid, UEEventCalendarListView) must be used as children of <UEEventCalendar>."
8
- );
9
- }
10
- return ctx;
11
- }
1
+ import { createComponentContext } from "../utils/createComponentContext.js";
2
+ const { key, useContext } = createComponentContext("EventCalendar");
3
+ export const eventCalendarContextKey = key;
4
+ export const useEventCalendarContext = useContext;
@@ -0,0 +1,46 @@
1
+ import type { FeedbackType, FeedbackTypeId, FeedbackSubmission, FeedbackSubmitResult, FeedbackWidgetPosition, CaptchaProvider } from "../types/feedback-widget.js";
2
+ export interface UseFeedbackWidgetOptions {
3
+ feedbackTypes: () => FeedbackType[];
4
+ position: () => FeedbackWidgetPosition;
5
+ showScreenshot: () => boolean;
6
+ emailPlaceholder: () => string;
7
+ messagePlaceholder: () => string;
8
+ captchaEnabled: () => boolean;
9
+ captchaProvider: () => CaptchaProvider | null;
10
+ captchaSiteKey: () => string | null;
11
+ submitEndpoint: () => string;
12
+ onSubmit: () => ((submission: FeedbackSubmission) => Promise<FeedbackSubmitResult> | FeedbackSubmitResult) | undefined;
13
+ onOpen: () => void;
14
+ onClose: () => void;
15
+ onSuccess: (submission: FeedbackSubmission) => void;
16
+ onError: (error: string) => void;
17
+ }
18
+ export declare function useFeedbackWidget(options: UseFeedbackWidgetOptions): {
19
+ isOpen: import("vue").Ref<boolean, boolean>;
20
+ isSubmitting: import("vue").Ref<boolean, boolean>;
21
+ isSubmitted: import("vue").Ref<boolean, boolean>;
22
+ submitError: import("vue").Ref<string | null, string | null>;
23
+ selectedType: import("vue").Ref<FeedbackTypeId | null, FeedbackTypeId | null>;
24
+ email: import("vue").Ref<string, string>;
25
+ message: import("vue").Ref<string, string>;
26
+ screenshot: import("vue").Ref<string | null, string | null>;
27
+ honeypot: import("vue").Ref<string, string>;
28
+ captchaToken: import("vue").Ref<string | null, string | null>;
29
+ feedbackTypes: import("vue").ComputedRef<FeedbackType[]>;
30
+ position: import("vue").ComputedRef<FeedbackWidgetPosition>;
31
+ showScreenshot: import("vue").ComputedRef<boolean>;
32
+ emailPlaceholder: import("vue").ComputedRef<string>;
33
+ messagePlaceholder: import("vue").ComputedRef<string>;
34
+ captchaEnabled: import("vue").ComputedRef<boolean>;
35
+ captchaProvider: import("vue").ComputedRef<CaptchaProvider | null>;
36
+ captchaSiteKey: import("vue").ComputedRef<string | null>;
37
+ isValid: import("vue").ComputedRef<boolean>;
38
+ open: () => void;
39
+ close: () => void;
40
+ toggle: () => void;
41
+ reset: () => void;
42
+ submit: () => Promise<void>;
43
+ captureScreenshot: () => Promise<void>;
44
+ removeScreenshot: () => void;
45
+ setCaptchaToken: (token: string) => void;
46
+ };
@@ -0,0 +1,137 @@
1
+ import { computed, ref } from "vue";
2
+ export function useFeedbackWidget(options) {
3
+ const isOpen = ref(false);
4
+ const isSubmitting = ref(false);
5
+ const isSubmitted = ref(false);
6
+ const submitError = ref(null);
7
+ const selectedType = ref(null);
8
+ const email = ref("");
9
+ const message = ref("");
10
+ const screenshot = ref(null);
11
+ const honeypot = ref("");
12
+ const captchaToken = ref(null);
13
+ const feedbackTypes = computed(() => options.feedbackTypes());
14
+ const position = computed(() => options.position());
15
+ const showScreenshot = computed(() => options.showScreenshot());
16
+ const emailPlaceholder = computed(() => options.emailPlaceholder());
17
+ const messagePlaceholder = computed(() => options.messagePlaceholder());
18
+ const captchaEnabled = computed(() => options.captchaEnabled());
19
+ const captchaProvider = computed(() => options.captchaProvider());
20
+ const captchaSiteKey = computed(() => options.captchaSiteKey());
21
+ const isValid = computed(() => {
22
+ return selectedType.value !== null && email.value.trim().length > 0 && message.value.trim().length > 0 && (!captchaEnabled.value || !!captchaToken.value);
23
+ });
24
+ function open() {
25
+ isOpen.value = true;
26
+ options.onOpen();
27
+ }
28
+ function close() {
29
+ isOpen.value = false;
30
+ options.onClose();
31
+ }
32
+ function toggle() {
33
+ if (isOpen.value) close();
34
+ else open();
35
+ }
36
+ function reset() {
37
+ selectedType.value = null;
38
+ email.value = "";
39
+ message.value = "";
40
+ screenshot.value = null;
41
+ honeypot.value = "";
42
+ captchaToken.value = null;
43
+ isSubmitted.value = false;
44
+ submitError.value = null;
45
+ }
46
+ async function captureScreenshot() {
47
+ try {
48
+ const { toPng } = await import("html-to-image");
49
+ const dataUrl = await toPng(document.body, {
50
+ quality: 0.8,
51
+ pixelRatio: 1
52
+ });
53
+ screenshot.value = dataUrl;
54
+ } catch {
55
+ }
56
+ }
57
+ function removeScreenshot() {
58
+ screenshot.value = null;
59
+ }
60
+ function setCaptchaToken(token) {
61
+ captchaToken.value = token;
62
+ }
63
+ async function submit() {
64
+ if (!isValid.value || isSubmitting.value) return;
65
+ isSubmitting.value = true;
66
+ submitError.value = null;
67
+ const submission = {
68
+ type: selectedType.value,
69
+ email: email.value.trim(),
70
+ message: message.value.trim(),
71
+ screenshot: screenshot.value ?? void 0,
72
+ _hp: honeypot.value,
73
+ captchaToken: captchaToken.value ?? void 0
74
+ };
75
+ try {
76
+ const customSubmit = options.onSubmit();
77
+ let result;
78
+ if (customSubmit) {
79
+ result = await customSubmit(submission);
80
+ } else {
81
+ const response = await $fetch(
82
+ options.submitEndpoint(),
83
+ {
84
+ method: "POST",
85
+ body: submission
86
+ }
87
+ );
88
+ result = response;
89
+ }
90
+ if (result.success) {
91
+ isSubmitted.value = true;
92
+ options.onSuccess(submission);
93
+ } else {
94
+ submitError.value = result.error ?? "Submission failed";
95
+ options.onError(submitError.value);
96
+ }
97
+ } catch (e) {
98
+ submitError.value = e?.data?.message || e?.message || "An error occurred";
99
+ options.onError(submitError.value);
100
+ } finally {
101
+ isSubmitting.value = false;
102
+ }
103
+ }
104
+ return {
105
+ // State
106
+ isOpen,
107
+ isSubmitting,
108
+ isSubmitted,
109
+ submitError,
110
+ // Form
111
+ selectedType,
112
+ email,
113
+ message,
114
+ screenshot,
115
+ honeypot,
116
+ captchaToken,
117
+ // Computed
118
+ feedbackTypes,
119
+ position,
120
+ showScreenshot,
121
+ emailPlaceholder,
122
+ messagePlaceholder,
123
+ captchaEnabled,
124
+ captchaProvider,
125
+ captchaSiteKey,
126
+ isValid,
127
+ // Actions
128
+ open,
129
+ close,
130
+ toggle,
131
+ reset,
132
+ submit,
133
+ captureScreenshot,
134
+ removeScreenshot,
135
+ setCaptchaToken
136
+ };
137
+ }
@@ -0,0 +1,3 @@
1
+ import type { FeedbackWidgetContext } from "../types/feedback-widget.js";
2
+ export declare const feedbackWidgetContextKey: import("vue").InjectionKey<FeedbackWidgetContext>;
3
+ export declare const useFeedbackWidgetContext: () => FeedbackWidgetContext;
@@ -0,0 +1,4 @@
1
+ import { createComponentContext } from "../utils/createComponentContext.js";
2
+ const { key, useContext } = createComponentContext("FeedbackWidget");
3
+ export const feedbackWidgetContextKey = key;
4
+ export const useFeedbackWidgetContext = useContext;
@@ -0,0 +1,52 @@
1
+ import type { DateValue } from "@internationalized/date";
2
+ import { CalendarDate } from "@internationalized/date";
3
+ import type { GanttTask, GanttDependency, GanttZoomLevel, NormalizedGanttTask, GanttTaskTreeNode, GanttLayout } from "../types/gantt-chart.js";
4
+ export interface UseGanttChartOptions {
5
+ tasks: () => GanttTask[];
6
+ dependencies: () => GanttDependency[];
7
+ modelValue: () => Date | string | DateValue | undefined;
8
+ zoomLevel: () => GanttZoomLevel;
9
+ zoomLevels: () => GanttZoomLevel[];
10
+ locale: () => string;
11
+ rowHeight: () => number;
12
+ expanded: () => (string | number)[] | undefined;
13
+ onUpdateModelValue: (date: CalendarDate) => void;
14
+ onUpdateZoomLevel: (level: GanttZoomLevel) => void;
15
+ onUpdateExpanded: (ids: (string | number)[]) => void;
16
+ }
17
+ export declare function useGanttChart(options: UseGanttChartOptions): {
18
+ displayDate: import("vue").ComputedRef<CalendarDate>;
19
+ zoomLevel: import("vue").ComputedRef<GanttZoomLevel>;
20
+ zoomLevels: import("vue").ComputedRef<GanttZoomLevel[]>;
21
+ rowHeight: import("vue").ComputedRef<number>;
22
+ normalizedTasks: import("vue").ComputedRef<NormalizedGanttTask[]>;
23
+ normalizedDependencies: import("vue").ComputedRef<GanttDependency[]>;
24
+ taskTree: import("vue").ComputedRef<GanttTaskTreeNode[]>;
25
+ expandedIds: import("vue").Ref<Set<string | number> & Omit<Set<string | number>, keyof Set<any>>, Set<string | number> | (Set<string | number> & Omit<Set<string | number>, keyof Set<any>>)>;
26
+ flatVisible: import("vue").ComputedRef<{
27
+ task: NormalizedGanttTask;
28
+ depth: number;
29
+ isGroup: boolean;
30
+ isExpanded: boolean;
31
+ childCount: number;
32
+ }[]>;
33
+ timelineStart: import("vue").ComputedRef<CalendarDate>;
34
+ timelineEnd: import("vue").ComputedRef<CalendarDate>;
35
+ layout: import("vue").ComputedRef<GanttLayout>;
36
+ headerTitle: import("vue").ComputedRef<string>;
37
+ unitWidth: import("vue").ComputedRef<number>;
38
+ bodyEl: import("vue").Ref<HTMLElement | null, HTMLElement | null>;
39
+ goToPrev: () => void;
40
+ goToNext: () => void;
41
+ goToToday: () => void;
42
+ setZoomLevel: (level: GanttZoomLevel) => void;
43
+ scrollToDate: (date: CalendarDate) => void;
44
+ scrollToTask: (taskId: string | number) => void;
45
+ toggleExpand: (id: string | number) => void;
46
+ expandAll: () => void;
47
+ collapseAll: () => void;
48
+ isExpanded: (id: string | number) => boolean;
49
+ dateToPixel: (date: CalendarDate) => number;
50
+ pixelToDate: (px: number) => CalendarDate;
51
+ getTaskStyle: (task: NormalizedGanttTask) => Record<string, string>;
52
+ };