nuxt-ui-elements-pro 0.1.9 → 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.
- package/dist/module.d.mts +5 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +243 -3
- package/dist/runtime/components/EventCalendar.d.vue.ts +4 -0
- package/dist/runtime/components/EventCalendar.vue +5 -2
- package/dist/runtime/components/EventCalendar.vue.d.ts +4 -0
- package/dist/runtime/components/FeedbackWidget.d.vue.ts +118 -0
- package/dist/runtime/components/FeedbackWidget.vue +141 -0
- package/dist/runtime/components/FeedbackWidget.vue.d.ts +118 -0
- package/dist/runtime/components/GanttChart.d.vue.ts +138 -0
- package/dist/runtime/components/GanttChart.vue +206 -0
- package/dist/runtime/components/GanttChart.vue.d.ts +138 -0
- package/dist/runtime/components/event-calendar/EventCalendarMonthView.d.vue.ts +3 -0
- package/dist/runtime/components/event-calendar/EventCalendarMonthView.vue +15 -6
- package/dist/runtime/components/event-calendar/EventCalendarMonthView.vue.d.ts +3 -0
- package/dist/runtime/components/event-calendar/EventCalendarTimeGrid.vue +23 -3
- package/dist/runtime/components/feedback-widget/FeedbackWidgetButton.d.vue.ts +22 -0
- package/dist/runtime/components/feedback-widget/FeedbackWidgetButton.vue +27 -0
- package/dist/runtime/components/feedback-widget/FeedbackWidgetButton.vue.d.ts +22 -0
- package/dist/runtime/components/feedback-widget/FeedbackWidgetPanel.d.vue.ts +42 -0
- package/dist/runtime/components/feedback-widget/FeedbackWidgetPanel.vue +288 -0
- package/dist/runtime/components/feedback-widget/FeedbackWidgetPanel.vue.d.ts +42 -0
- package/dist/runtime/components/gantt-chart/GanttChartDependencies.d.vue.ts +16 -0
- package/dist/runtime/components/gantt-chart/GanttChartDependencies.vue +70 -0
- package/dist/runtime/components/gantt-chart/GanttChartDependencies.vue.d.ts +16 -0
- package/dist/runtime/components/gantt-chart/GanttChartHeader.d.vue.ts +41 -0
- package/dist/runtime/components/gantt-chart/GanttChartHeader.vue +56 -0
- package/dist/runtime/components/gantt-chart/GanttChartHeader.vue.d.ts +41 -0
- package/dist/runtime/components/gantt-chart/GanttChartTimeline.d.vue.ts +34 -0
- package/dist/runtime/components/gantt-chart/GanttChartTimeline.vue +193 -0
- package/dist/runtime/components/gantt-chart/GanttChartTimeline.vue.d.ts +34 -0
- package/dist/runtime/composables/useEventCalendar.d.ts +2 -0
- package/dist/runtime/composables/useEventCalendar.js +8 -6
- package/dist/runtime/composables/useEventCalendarContext.d.ts +2 -7
- package/dist/runtime/composables/useEventCalendarContext.js +4 -11
- package/dist/runtime/composables/useFeedbackWidget.d.ts +46 -0
- package/dist/runtime/composables/useFeedbackWidget.js +137 -0
- package/dist/runtime/composables/useFeedbackWidgetContext.d.ts +3 -0
- package/dist/runtime/composables/useFeedbackWidgetContext.js +4 -0
- package/dist/runtime/composables/useGanttChart.d.ts +52 -0
- package/dist/runtime/composables/useGanttChart.js +224 -0
- package/dist/runtime/composables/useGanttChartContext.d.ts +3 -0
- package/dist/runtime/composables/useGanttChartContext.js +4 -0
- package/dist/runtime/composables/useGanttChartDragDrop.d.ts +28 -0
- package/dist/runtime/composables/useGanttChartDragDrop.js +68 -0
- package/dist/runtime/composables/useGanttChartKeyboard.d.ts +14 -0
- package/dist/runtime/composables/useGanttChartKeyboard.js +92 -0
- package/dist/runtime/composables/useGanttChartResize.d.ts +26 -0
- package/dist/runtime/composables/useGanttChartResize.js +89 -0
- package/dist/runtime/composables/useOrgChartContext.d.ts +2 -7
- package/dist/runtime/composables/useOrgChartContext.js +4 -11
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/server/api/_feedback.post.d.ts +3 -0
- package/dist/runtime/server/api/_feedback.post.js +115 -0
- package/dist/runtime/server/nodemailer.d.ts +29 -0
- package/dist/runtime/server/utils/feedback-captcha.d.ts +1 -0
- package/dist/runtime/server/utils/feedback-captcha.js +27 -0
- package/dist/runtime/server/utils/feedback-rate-limit.d.ts +4 -0
- package/dist/runtime/server/utils/feedback-rate-limit.js +26 -0
- package/dist/runtime/types/event-calendar.d.ts +10 -4
- package/dist/runtime/types/feedback-widget.d.ts +110 -0
- package/dist/runtime/types/feedback-widget.js +0 -0
- package/dist/runtime/types/gantt-chart.d.ts +223 -0
- package/dist/runtime/types/gantt-chart.js +0 -0
- package/dist/runtime/types/index.d.ts +4 -0
- package/dist/runtime/types/index.js +4 -0
- package/dist/runtime/utils/createComponentContext.d.ts +15 -0
- package/dist/runtime/utils/createComponentContext.js +17 -0
- package/dist/runtime/utils/date.d.ts +10 -0
- package/dist/runtime/utils/date.js +9 -0
- package/dist/runtime/utils/event-calendar.d.ts +1 -9
- package/dist/runtime/utils/event-calendar.js +2 -9
- package/dist/runtime/utils/gantt-chart.d.ts +85 -0
- package/dist/runtime/utils/gantt-chart.js +549 -0
- package/dist/runtime/utils/recurrence.js +2 -1
- package/package.json +17 -8
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { Primitive } from "reka-ui";
|
|
3
|
-
import {} from "vue";
|
|
3
|
+
import { ref, onMounted, watch, nextTick } from "vue";
|
|
4
4
|
import { useEventCalendarContext } from "../../composables/useEventCalendarContext";
|
|
5
5
|
import { format } from "#std/date";
|
|
6
6
|
</script>
|
|
@@ -11,6 +11,26 @@ const props = defineProps({
|
|
|
11
11
|
});
|
|
12
12
|
defineSlots();
|
|
13
13
|
const ctx = useEventCalendarContext();
|
|
14
|
+
const timeGridEl = ref(null);
|
|
15
|
+
function scrollToTime() {
|
|
16
|
+
const el = timeGridEl.value;
|
|
17
|
+
if (!el) return;
|
|
18
|
+
const config = ctx.view.value === "day" ? ctx.dayConfig.value : ctx.weekConfig.value;
|
|
19
|
+
const scrollTime = config.scrollTime;
|
|
20
|
+
if (scrollTime == null) return;
|
|
21
|
+
const clampedScrollTime = Math.max(config.startHour, Math.min(scrollTime, config.endHour - 1));
|
|
22
|
+
const slotsPerHour = 60 / config.slotDuration;
|
|
23
|
+
const offsetHours = clampedScrollTime - config.startHour;
|
|
24
|
+
el.scrollTop = offsetHours * slotsPerHour * ctx.SLOT_HEIGHT;
|
|
25
|
+
}
|
|
26
|
+
onMounted(() => {
|
|
27
|
+
nextTick(scrollToTime);
|
|
28
|
+
});
|
|
29
|
+
watch(() => ctx.view.value, (v) => {
|
|
30
|
+
if (v === "week" || v === "day") {
|
|
31
|
+
nextTick(scrollToTime);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
14
34
|
</script>
|
|
15
35
|
|
|
16
36
|
<template>
|
|
@@ -163,8 +183,8 @@ const ctx = useEventCalendarContext();
|
|
|
163
183
|
</div>
|
|
164
184
|
|
|
165
185
|
<!-- Time grid -->
|
|
166
|
-
<div class="
|
|
167
|
-
<div data-slot="timeGrid" :class="ctx.ui.value.timeGrid({ class: ctx.propUi.value?.timeGrid })" @selectstart.prevent>
|
|
186
|
+
<div data-slot="timeGridWrapper" :class="ctx.ui.value.timeGridWrapper({ class: ctx.propUi.value?.timeGridWrapper })">
|
|
187
|
+
<div ref="timeGridEl" data-slot="timeGrid" :class="ctx.ui.value.timeGrid({ class: ctx.propUi.value?.timeGrid })" @selectstart.prevent>
|
|
168
188
|
<!-- Time gutter -->
|
|
169
189
|
<div data-slot="timeGutter" :class="ctx.ui.value.timeGutter({ class: ctx.propUi.value?.timeGutter })">
|
|
170
190
|
<div
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
icon: string;
|
|
3
|
+
label: string;
|
|
4
|
+
};
|
|
5
|
+
declare var __VLS_1: {
|
|
6
|
+
open: () => void;
|
|
7
|
+
close: () => void;
|
|
8
|
+
toggle: () => void;
|
|
9
|
+
isOpen: boolean;
|
|
10
|
+
};
|
|
11
|
+
type __VLS_Slots = {} & {
|
|
12
|
+
default?: (props: typeof __VLS_1) => any;
|
|
13
|
+
};
|
|
14
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
15
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
16
|
+
declare const _default: typeof __VLS_export;
|
|
17
|
+
export default _default;
|
|
18
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
19
|
+
new (): {
|
|
20
|
+
$slots: S;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useFeedbackWidgetContext } from "../../composables/useFeedbackWidgetContext";
|
|
3
|
+
defineProps({
|
|
4
|
+
icon: { type: String, required: true },
|
|
5
|
+
label: { type: String, required: true }
|
|
6
|
+
});
|
|
7
|
+
const ctx = useFeedbackWidgetContext();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<slot
|
|
12
|
+
:open="ctx.open"
|
|
13
|
+
:close="ctx.close"
|
|
14
|
+
:toggle="ctx.toggle"
|
|
15
|
+
:is-open="ctx.isOpen.value"
|
|
16
|
+
>
|
|
17
|
+
<button
|
|
18
|
+
type="button"
|
|
19
|
+
data-slot="button"
|
|
20
|
+
:class="ctx.ui.value.button({ class: ctx.propUi.value?.button })"
|
|
21
|
+
:aria-label="label"
|
|
22
|
+
@click="ctx.toggle"
|
|
23
|
+
>
|
|
24
|
+
<UIcon :name="icon" :class="ctx.ui.value.buttonIcon({ class: ctx.propUi.value?.buttonIcon })" />
|
|
25
|
+
</button>
|
|
26
|
+
</slot>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
icon: string;
|
|
3
|
+
label: string;
|
|
4
|
+
};
|
|
5
|
+
declare var __VLS_1: {
|
|
6
|
+
open: () => void;
|
|
7
|
+
close: () => void;
|
|
8
|
+
toggle: () => void;
|
|
9
|
+
isOpen: boolean;
|
|
10
|
+
};
|
|
11
|
+
type __VLS_Slots = {} & {
|
|
12
|
+
default?: (props: typeof __VLS_1) => any;
|
|
13
|
+
};
|
|
14
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
15
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
16
|
+
declare const _default: typeof __VLS_export;
|
|
17
|
+
export default _default;
|
|
18
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
19
|
+
new (): {
|
|
20
|
+
$slots: S;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
title: string;
|
|
3
|
+
};
|
|
4
|
+
declare var __VLS_1: {
|
|
5
|
+
reset: () => void;
|
|
6
|
+
close: () => void;
|
|
7
|
+
}, __VLS_16: {
|
|
8
|
+
title: string;
|
|
9
|
+
close: () => void;
|
|
10
|
+
}, __VLS_25: {
|
|
11
|
+
types: import("../../types/feedback-widget.js").FeedbackType[];
|
|
12
|
+
selected: (string & {}) | null;
|
|
13
|
+
select: (id: string) => string;
|
|
14
|
+
}, __VLS_32: {
|
|
15
|
+
email: string;
|
|
16
|
+
message: string;
|
|
17
|
+
screenshot: string | null;
|
|
18
|
+
}, __VLS_69: {
|
|
19
|
+
submit: () => Promise<void>;
|
|
20
|
+
isValid: boolean;
|
|
21
|
+
isSubmitting: boolean;
|
|
22
|
+
};
|
|
23
|
+
type __VLS_Slots = {} & {
|
|
24
|
+
success?: (props: typeof __VLS_1) => any;
|
|
25
|
+
} & {
|
|
26
|
+
header?: (props: typeof __VLS_16) => any;
|
|
27
|
+
} & {
|
|
28
|
+
'type-selector'?: (props: typeof __VLS_25) => any;
|
|
29
|
+
} & {
|
|
30
|
+
form?: (props: typeof __VLS_32) => any;
|
|
31
|
+
} & {
|
|
32
|
+
'submit-area'?: (props: typeof __VLS_69) => any;
|
|
33
|
+
};
|
|
34
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
35
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
36
|
+
declare const _default: typeof __VLS_export;
|
|
37
|
+
export default _default;
|
|
38
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
39
|
+
new (): {
|
|
40
|
+
$slots: S;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { onMounted, onUnmounted, ref, watch } from "vue";
|
|
3
|
+
import { useFeedbackWidgetContext } from "../../composables/useFeedbackWidgetContext";
|
|
4
|
+
defineProps({
|
|
5
|
+
title: { type: String, required: true }
|
|
6
|
+
});
|
|
7
|
+
const ctx = useFeedbackWidgetContext();
|
|
8
|
+
const captchaContainerRef = ref(null);
|
|
9
|
+
let captchaWidgetId = null;
|
|
10
|
+
const CAPTCHA_SCRIPTS = {
|
|
11
|
+
turnstile: "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit",
|
|
12
|
+
recaptcha: "https://www.google.com/recaptcha/api.js?render=explicit",
|
|
13
|
+
hcaptcha: "https://js.hcaptcha.com/1/api.js?render=explicit"
|
|
14
|
+
};
|
|
15
|
+
function loadCaptchaScript(provider) {
|
|
16
|
+
const existingScript = document.querySelector(`script[data-captcha-provider="${provider}"]`);
|
|
17
|
+
if (existingScript) return Promise.resolve();
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const script = document.createElement("script");
|
|
20
|
+
script.src = CAPTCHA_SCRIPTS[provider];
|
|
21
|
+
script.async = true;
|
|
22
|
+
script.defer = true;
|
|
23
|
+
script.dataset.captchaProvider = provider;
|
|
24
|
+
script.onload = () => resolve();
|
|
25
|
+
script.onerror = () => reject(new Error(`Failed to load ${provider} script`));
|
|
26
|
+
document.head.appendChild(script);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function renderCaptcha() {
|
|
30
|
+
if (!captchaContainerRef.value || !ctx.captchaEnabled.value) return;
|
|
31
|
+
const provider = ctx.captchaProvider.value;
|
|
32
|
+
const siteKey = ctx.captchaSiteKey.value;
|
|
33
|
+
if (!provider || !siteKey) return;
|
|
34
|
+
if (captchaContainerRef.value) {
|
|
35
|
+
captchaContainerRef.value.innerHTML = "";
|
|
36
|
+
}
|
|
37
|
+
const callback = (token) => ctx.setCaptchaToken(token);
|
|
38
|
+
if (provider === "turnstile" && window.turnstile) {
|
|
39
|
+
captchaWidgetId = window.turnstile.render(captchaContainerRef.value, {
|
|
40
|
+
sitekey: siteKey,
|
|
41
|
+
callback
|
|
42
|
+
});
|
|
43
|
+
} else if (provider === "recaptcha" && window.grecaptcha) {
|
|
44
|
+
captchaWidgetId = window.grecaptcha.render(captchaContainerRef.value, {
|
|
45
|
+
sitekey: siteKey,
|
|
46
|
+
callback
|
|
47
|
+
});
|
|
48
|
+
} else if (provider === "hcaptcha" && window.hcaptcha) {
|
|
49
|
+
captchaWidgetId = window.hcaptcha.render(captchaContainerRef.value, {
|
|
50
|
+
sitekey: siteKey,
|
|
51
|
+
callback
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
watch(() => ctx.isOpen.value, async (open) => {
|
|
56
|
+
if (open && ctx.captchaEnabled.value && ctx.captchaProvider.value) {
|
|
57
|
+
try {
|
|
58
|
+
await loadCaptchaScript(ctx.captchaProvider.value);
|
|
59
|
+
setTimeout(renderCaptcha, 100);
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
function onKeydown(e) {
|
|
65
|
+
if (e.key === "Escape" && ctx.isOpen.value) {
|
|
66
|
+
ctx.close();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
onMounted(() => {
|
|
70
|
+
document.addEventListener("keydown", onKeydown);
|
|
71
|
+
});
|
|
72
|
+
onUnmounted(() => {
|
|
73
|
+
document.removeEventListener("keydown", onKeydown);
|
|
74
|
+
});
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<template>
|
|
78
|
+
<div
|
|
79
|
+
data-slot="panel"
|
|
80
|
+
:class="[
|
|
81
|
+
ctx.ui.value.panel({ class: ctx.propUi.value?.panel }),
|
|
82
|
+
ctx.isOpen.value ? ctx.ui.value.panelVisible({ class: ctx.propUi.value?.panelVisible }) : ctx.ui.value.panelHidden({ class: ctx.propUi.value?.panelHidden })
|
|
83
|
+
]"
|
|
84
|
+
>
|
|
85
|
+
<!-- Success state -->
|
|
86
|
+
<template v-if="ctx.isSubmitted.value">
|
|
87
|
+
<slot name="success" :reset="ctx.reset" :close="ctx.close">
|
|
88
|
+
<div data-slot="successState" :class="ctx.ui.value.successState({ class: ctx.propUi.value?.successState })">
|
|
89
|
+
<UIcon name="i-lucide-check-circle-2" :class="ctx.ui.value.successIcon({ class: ctx.propUi.value?.successIcon })" />
|
|
90
|
+
<p :class="ctx.ui.value.successTitle({ class: ctx.propUi.value?.successTitle })">Thank you!</p>
|
|
91
|
+
<p :class="ctx.ui.value.successMessage({ class: ctx.propUi.value?.successMessage })">
|
|
92
|
+
Your feedback has been submitted successfully.
|
|
93
|
+
</p>
|
|
94
|
+
<UButton size="sm" color="neutral" variant="outline" @click="ctx.reset">
|
|
95
|
+
Send more feedback
|
|
96
|
+
</UButton>
|
|
97
|
+
</div>
|
|
98
|
+
</slot>
|
|
99
|
+
</template>
|
|
100
|
+
|
|
101
|
+
<!-- Main form flow -->
|
|
102
|
+
<template v-else>
|
|
103
|
+
<!-- Header -->
|
|
104
|
+
<slot name="header" :title="title" :close="ctx.close">
|
|
105
|
+
<div data-slot="panelHeader" :class="ctx.ui.value.panelHeader({ class: ctx.propUi.value?.panelHeader })">
|
|
106
|
+
<span :class="ctx.ui.value.panelTitle({ class: ctx.propUi.value?.panelTitle })">{{ title }}</span>
|
|
107
|
+
<UButton
|
|
108
|
+
size="xs"
|
|
109
|
+
color="neutral"
|
|
110
|
+
variant="ghost"
|
|
111
|
+
icon="i-lucide-x"
|
|
112
|
+
square
|
|
113
|
+
:class="ctx.ui.value.panelCloseButton({ class: ctx.propUi.value?.panelCloseButton })"
|
|
114
|
+
aria-label="Close feedback panel"
|
|
115
|
+
@click="ctx.close"
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
</slot>
|
|
119
|
+
|
|
120
|
+
<!-- Type selector (step 1) -->
|
|
121
|
+
<template v-if="!ctx.selectedType.value">
|
|
122
|
+
<slot
|
|
123
|
+
name="type-selector"
|
|
124
|
+
:types="ctx.feedbackTypes.value"
|
|
125
|
+
:selected="ctx.selectedType.value"
|
|
126
|
+
:select="(id) => ctx.selectedType.value = id"
|
|
127
|
+
>
|
|
128
|
+
<div data-slot="typeSelector" :class="ctx.ui.value.typeSelector({ class: ctx.propUi.value?.typeSelector })">
|
|
129
|
+
<button
|
|
130
|
+
v-for="feedbackType in ctx.feedbackTypes.value"
|
|
131
|
+
:key="feedbackType.id"
|
|
132
|
+
type="button"
|
|
133
|
+
data-slot="typeOption"
|
|
134
|
+
:class="[
|
|
135
|
+
ctx.ui.value.typeOption({ class: ctx.propUi.value?.typeOption }),
|
|
136
|
+
ctx.selectedType.value === feedbackType.id && ctx.ui.value.typeOptionSelected({ class: ctx.propUi.value?.typeOptionSelected })
|
|
137
|
+
]"
|
|
138
|
+
@click="ctx.selectedType.value = feedbackType.id"
|
|
139
|
+
>
|
|
140
|
+
<UIcon
|
|
141
|
+
v-if="feedbackType.icon"
|
|
142
|
+
:name="feedbackType.icon"
|
|
143
|
+
:class="ctx.ui.value.typeOptionIcon({ class: ctx.propUi.value?.typeOptionIcon })"
|
|
144
|
+
/>
|
|
145
|
+
<div>
|
|
146
|
+
<div :class="ctx.ui.value.typeOptionLabel({ class: ctx.propUi.value?.typeOptionLabel })">
|
|
147
|
+
{{ feedbackType.label }}
|
|
148
|
+
</div>
|
|
149
|
+
<div
|
|
150
|
+
v-if="feedbackType.description"
|
|
151
|
+
:class="ctx.ui.value.typeOptionDescription({ class: ctx.propUi.value?.typeOptionDescription })"
|
|
152
|
+
>
|
|
153
|
+
{{ feedbackType.description }}
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</button>
|
|
157
|
+
</div>
|
|
158
|
+
</slot>
|
|
159
|
+
</template>
|
|
160
|
+
|
|
161
|
+
<!-- Form (step 2) -->
|
|
162
|
+
<template v-else>
|
|
163
|
+
<slot
|
|
164
|
+
name="form"
|
|
165
|
+
:email="ctx.email.value"
|
|
166
|
+
:message="ctx.message.value"
|
|
167
|
+
:screenshot="ctx.screenshot.value"
|
|
168
|
+
>
|
|
169
|
+
<div data-slot="form" :class="ctx.ui.value.form({ class: ctx.propUi.value?.form })">
|
|
170
|
+
<!-- Back button -->
|
|
171
|
+
<UButton
|
|
172
|
+
size="xs"
|
|
173
|
+
color="neutral"
|
|
174
|
+
variant="ghost"
|
|
175
|
+
icon="i-lucide-arrow-left"
|
|
176
|
+
label="Back"
|
|
177
|
+
data-slot="backButton"
|
|
178
|
+
:class="ctx.ui.value.backButton({ class: ctx.propUi.value?.backButton })"
|
|
179
|
+
@click="ctx.selectedType.value = null"
|
|
180
|
+
/>
|
|
181
|
+
|
|
182
|
+
<!-- Email -->
|
|
183
|
+
<div data-slot="formField" :class="ctx.ui.value.formField({ class: ctx.propUi.value?.formField })">
|
|
184
|
+
<label :class="ctx.ui.value.formLabel({ class: ctx.propUi.value?.formLabel })">Email</label>
|
|
185
|
+
<UInput
|
|
186
|
+
:model-value="ctx.email.value"
|
|
187
|
+
type="email"
|
|
188
|
+
:color="ctx.color.value"
|
|
189
|
+
:placeholder="ctx.emailPlaceholder.value"
|
|
190
|
+
data-slot="formInput"
|
|
191
|
+
@update:model-value="ctx.email.value = $event"
|
|
192
|
+
/>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<!-- Message -->
|
|
196
|
+
<div data-slot="formField" :class="ctx.ui.value.formField({ class: ctx.propUi.value?.formField })">
|
|
197
|
+
<label :class="ctx.ui.value.formLabel({ class: ctx.propUi.value?.formLabel })">Message</label>
|
|
198
|
+
<UTextarea
|
|
199
|
+
:model-value="ctx.message.value"
|
|
200
|
+
:color="ctx.color.value"
|
|
201
|
+
:placeholder="ctx.messagePlaceholder.value"
|
|
202
|
+
:rows="3"
|
|
203
|
+
data-slot="formTextarea"
|
|
204
|
+
class="resize-none"
|
|
205
|
+
@update:model-value="ctx.message.value = $event"
|
|
206
|
+
/>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
<!-- Screenshot -->
|
|
210
|
+
<template v-if="ctx.showScreenshot.value">
|
|
211
|
+
<div v-if="ctx.screenshot.value" :class="ctx.ui.value.screenshotPreview({ class: ctx.propUi.value?.screenshotPreview })">
|
|
212
|
+
<img :src="ctx.screenshot.value" alt="Screenshot preview" :class="ctx.ui.value.screenshotImage({ class: ctx.propUi.value?.screenshotImage })">
|
|
213
|
+
<UButton
|
|
214
|
+
size="xs"
|
|
215
|
+
color="neutral"
|
|
216
|
+
variant="ghost"
|
|
217
|
+
icon="i-lucide-x"
|
|
218
|
+
square
|
|
219
|
+
:class="ctx.ui.value.screenshotRemove({ class: ctx.propUi.value?.screenshotRemove })"
|
|
220
|
+
aria-label="Remove screenshot"
|
|
221
|
+
@click="ctx.removeScreenshot"
|
|
222
|
+
/>
|
|
223
|
+
</div>
|
|
224
|
+
<UButton
|
|
225
|
+
v-else
|
|
226
|
+
size="xs"
|
|
227
|
+
color="neutral"
|
|
228
|
+
variant="ghost"
|
|
229
|
+
icon="i-lucide-camera"
|
|
230
|
+
label="Capture screenshot"
|
|
231
|
+
:class="ctx.ui.value.screenshotButton({ class: ctx.propUi.value?.screenshotButton })"
|
|
232
|
+
@click="ctx.captureScreenshot"
|
|
233
|
+
/>
|
|
234
|
+
</template>
|
|
235
|
+
|
|
236
|
+
<!-- Honeypot (invisible to real users) -->
|
|
237
|
+
<div :class="ctx.ui.value.honeypot({ class: ctx.propUi.value?.honeypot })" aria-hidden="true" tabindex="-1">
|
|
238
|
+
<label for="_feedback_hp">Leave this empty</label>
|
|
239
|
+
<input
|
|
240
|
+
id="_feedback_hp"
|
|
241
|
+
:value="ctx.honeypot.value"
|
|
242
|
+
type="text"
|
|
243
|
+
name="_hp"
|
|
244
|
+
autocomplete="off"
|
|
245
|
+
tabindex="-1"
|
|
246
|
+
@input="ctx.honeypot.value = $event.target.value"
|
|
247
|
+
>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
<!-- CAPTCHA -->
|
|
251
|
+
<div
|
|
252
|
+
v-if="ctx.captchaEnabled.value"
|
|
253
|
+
ref="captchaContainerRef"
|
|
254
|
+
data-slot="captchaContainer"
|
|
255
|
+
:class="ctx.ui.value.captchaContainer({ class: ctx.propUi.value?.captchaContainer })"
|
|
256
|
+
/>
|
|
257
|
+
</div>
|
|
258
|
+
</slot>
|
|
259
|
+
|
|
260
|
+
<!-- Error message -->
|
|
261
|
+
<p v-if="ctx.submitError.value" data-slot="errorMessage" :class="ctx.ui.value.errorMessage({ class: ctx.propUi.value?.errorMessage })">
|
|
262
|
+
{{ ctx.submitError.value }}
|
|
263
|
+
</p>
|
|
264
|
+
|
|
265
|
+
<!-- Submit area -->
|
|
266
|
+
<slot
|
|
267
|
+
name="submit-area"
|
|
268
|
+
:submit="ctx.submit"
|
|
269
|
+
:is-valid="ctx.isValid.value"
|
|
270
|
+
:is-submitting="ctx.isSubmitting.value"
|
|
271
|
+
>
|
|
272
|
+
<div data-slot="submitArea" :class="ctx.ui.value.submitArea({ class: ctx.propUi.value?.submitArea })">
|
|
273
|
+
<span class="text-xs text-muted">
|
|
274
|
+
{{ ctx.feedbackTypes.value.find((t) => t.id === ctx.selectedType.value)?.label }}
|
|
275
|
+
</span>
|
|
276
|
+
<UButton
|
|
277
|
+
size="sm"
|
|
278
|
+
:color="ctx.color.value"
|
|
279
|
+
:disabled="!ctx.isValid.value || ctx.isSubmitting.value"
|
|
280
|
+
:label="ctx.isSubmitting.value ? 'Sending...' : 'Submit'"
|
|
281
|
+
@click="ctx.submit"
|
|
282
|
+
/>
|
|
283
|
+
</div>
|
|
284
|
+
</slot>
|
|
285
|
+
</template>
|
|
286
|
+
</template>
|
|
287
|
+
</div>
|
|
288
|
+
</template>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
title: string;
|
|
3
|
+
};
|
|
4
|
+
declare var __VLS_1: {
|
|
5
|
+
reset: () => void;
|
|
6
|
+
close: () => void;
|
|
7
|
+
}, __VLS_16: {
|
|
8
|
+
title: string;
|
|
9
|
+
close: () => void;
|
|
10
|
+
}, __VLS_25: {
|
|
11
|
+
types: import("../../types/feedback-widget.js").FeedbackType[];
|
|
12
|
+
selected: (string & {}) | null;
|
|
13
|
+
select: (id: string) => string;
|
|
14
|
+
}, __VLS_32: {
|
|
15
|
+
email: string;
|
|
16
|
+
message: string;
|
|
17
|
+
screenshot: string | null;
|
|
18
|
+
}, __VLS_69: {
|
|
19
|
+
submit: () => Promise<void>;
|
|
20
|
+
isValid: boolean;
|
|
21
|
+
isSubmitting: boolean;
|
|
22
|
+
};
|
|
23
|
+
type __VLS_Slots = {} & {
|
|
24
|
+
success?: (props: typeof __VLS_1) => any;
|
|
25
|
+
} & {
|
|
26
|
+
header?: (props: typeof __VLS_16) => any;
|
|
27
|
+
} & {
|
|
28
|
+
'type-selector'?: (props: typeof __VLS_25) => any;
|
|
29
|
+
} & {
|
|
30
|
+
form?: (props: typeof __VLS_32) => any;
|
|
31
|
+
} & {
|
|
32
|
+
'submit-area'?: (props: typeof __VLS_69) => any;
|
|
33
|
+
};
|
|
34
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
35
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
36
|
+
declare const _default: typeof __VLS_export;
|
|
37
|
+
export default _default;
|
|
38
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
39
|
+
new (): {
|
|
40
|
+
$slots: S;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { GanttDependencyPath } from "../../types/gantt-chart.js";
|
|
2
|
+
declare var __VLS_1: {
|
|
3
|
+
paths: GanttDependencyPath[];
|
|
4
|
+
};
|
|
5
|
+
type __VLS_Slots = {} & {
|
|
6
|
+
default?: (props: typeof __VLS_1) => any;
|
|
7
|
+
};
|
|
8
|
+
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>;
|
|
9
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
10
|
+
declare const _default: typeof __VLS_export;
|
|
11
|
+
export default _default;
|
|
12
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
13
|
+
new (): {
|
|
14
|
+
$slots: S;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { useGanttChartContext } from "../../composables/useGanttChartContext";
|
|
4
|
+
import { buildDependencyPath } from "../../utils/gantt-chart";
|
|
5
|
+
const ctx = useGanttChartContext();
|
|
6
|
+
const activePaths = computed(() => {
|
|
7
|
+
const basePaths = ctx.layout.value.dependencyPaths;
|
|
8
|
+
const dragId = ctx.draggedTaskId.value;
|
|
9
|
+
const dragPreview = ctx.dragPreview.value;
|
|
10
|
+
const resizeId = ctx.resizingTaskId.value;
|
|
11
|
+
const resizePreview = ctx.resizePreview.value;
|
|
12
|
+
if ((!dragId || !dragPreview) && (!resizeId || !resizePreview)) {
|
|
13
|
+
return basePaths;
|
|
14
|
+
}
|
|
15
|
+
const activeId = dragId ?? resizeId;
|
|
16
|
+
const preview = dragPreview ?? resizePreview;
|
|
17
|
+
if (!activeId || !preview) return basePaths;
|
|
18
|
+
return basePaths.map((dep) => {
|
|
19
|
+
const isFromActive = dep.from.task.id === activeId;
|
|
20
|
+
const isToActive = dep.to.task.id === activeId;
|
|
21
|
+
if (!isFromActive && !isToActive) return dep;
|
|
22
|
+
const adjustedFrom = isFromActive ? { ...dep.from, leftPx: preview.leftPx, widthPx: preview.widthPx } : dep.from;
|
|
23
|
+
const adjustedTo = isToActive ? { ...dep.to, leftPx: preview.leftPx, widthPx: preview.widthPx } : dep.to;
|
|
24
|
+
const result = buildDependencyPath(adjustedFrom, adjustedTo, ctx.rowHeight.value);
|
|
25
|
+
return { ...dep, path: result.path, fromPoint: result.fromPoint, toPoint: result.toPoint };
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<template>
|
|
31
|
+
<svg
|
|
32
|
+
v-if="activePaths.length > 0"
|
|
33
|
+
data-slot="dependencyLayer"
|
|
34
|
+
:class="ctx.ui.value.dependencyLayer({ class: ctx.propUi.value?.dependencyLayer })"
|
|
35
|
+
:width="ctx.layout.value.totalWidth"
|
|
36
|
+
:height="ctx.layout.value.totalHeight"
|
|
37
|
+
:viewBox="`0 0 ${ctx.layout.value.totalWidth} ${ctx.layout.value.totalHeight}`"
|
|
38
|
+
aria-hidden="true"
|
|
39
|
+
>
|
|
40
|
+
<!-- Arrowhead marker definition -->
|
|
41
|
+
<defs>
|
|
42
|
+
<marker
|
|
43
|
+
id="gantt-arrowhead"
|
|
44
|
+
viewBox="0 0 10 10"
|
|
45
|
+
refX="10"
|
|
46
|
+
refY="5"
|
|
47
|
+
markerWidth="6"
|
|
48
|
+
markerHeight="6"
|
|
49
|
+
orient="auto-start-reverse"
|
|
50
|
+
>
|
|
51
|
+
<path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor" class="text-muted" />
|
|
52
|
+
</marker>
|
|
53
|
+
</defs>
|
|
54
|
+
|
|
55
|
+
<slot :paths="activePaths">
|
|
56
|
+
<template v-for="dep in activePaths" :key="`${dep.dependency.fromId}-${dep.dependency.toId}`">
|
|
57
|
+
<path
|
|
58
|
+
data-slot="dependencyArrow"
|
|
59
|
+
:d="dep.path"
|
|
60
|
+
:class="ctx.ui.value.dependencyArrow({ class: ctx.propUi.value?.dependencyArrow })"
|
|
61
|
+
stroke-width="1.5"
|
|
62
|
+
marker-end="url(#gantt-arrowhead)"
|
|
63
|
+
/>
|
|
64
|
+
<!-- Connection dots at both ends -->
|
|
65
|
+
<circle :cx="dep.fromPoint.x" :cy="dep.fromPoint.y" r="3" fill="currentColor" class="text-muted" />
|
|
66
|
+
<circle :cx="dep.toPoint.x" :cy="dep.toPoint.y" r="3" fill="currentColor" class="text-muted" />
|
|
67
|
+
</template>
|
|
68
|
+
</slot>
|
|
69
|
+
</svg>
|
|
70
|
+
</template>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { GanttDependencyPath } from "../../types/gantt-chart.js";
|
|
2
|
+
declare var __VLS_1: {
|
|
3
|
+
paths: GanttDependencyPath[];
|
|
4
|
+
};
|
|
5
|
+
type __VLS_Slots = {} & {
|
|
6
|
+
default?: (props: typeof __VLS_1) => any;
|
|
7
|
+
};
|
|
8
|
+
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>;
|
|
9
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
10
|
+
declare const _default: typeof __VLS_export;
|
|
11
|
+
export default _default;
|
|
12
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
13
|
+
new (): {
|
|
14
|
+
$slots: S;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { GanttZoomLevel } from "../../types/gantt-chart.js";
|
|
2
|
+
import type { Component } from "vue";
|
|
3
|
+
export interface GanttChartHeaderProps {
|
|
4
|
+
/** Rendered element type @defaultValue 'div' */
|
|
5
|
+
as?: string | Component;
|
|
6
|
+
}
|
|
7
|
+
export interface GanttChartHeaderSlots {
|
|
8
|
+
default: (props: {
|
|
9
|
+
title: string;
|
|
10
|
+
prev: () => void;
|
|
11
|
+
next: () => void;
|
|
12
|
+
today: () => void;
|
|
13
|
+
zoomLevel: GanttZoomLevel;
|
|
14
|
+
setZoomLevel: (level: GanttZoomLevel) => void;
|
|
15
|
+
zoomLevels: GanttZoomLevel[];
|
|
16
|
+
}) => any;
|
|
17
|
+
/** Replace just the title element */
|
|
18
|
+
title: (props: {
|
|
19
|
+
title: string;
|
|
20
|
+
}) => any;
|
|
21
|
+
/** Replace just the navigation buttons */
|
|
22
|
+
nav: (props: {
|
|
23
|
+
prev: () => void;
|
|
24
|
+
next: () => void;
|
|
25
|
+
today: () => void;
|
|
26
|
+
}) => any;
|
|
27
|
+
/** Replace just the zoom level switcher */
|
|
28
|
+
"zoom-switcher": (props: {
|
|
29
|
+
zoomLevel: GanttZoomLevel;
|
|
30
|
+
setZoomLevel: (level: GanttZoomLevel) => void;
|
|
31
|
+
zoomLevels: GanttZoomLevel[];
|
|
32
|
+
}) => any;
|
|
33
|
+
}
|
|
34
|
+
declare const _default: typeof __VLS_export;
|
|
35
|
+
export default _default;
|
|
36
|
+
declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<GanttChartHeaderProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<GanttChartHeaderProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, GanttChartHeaderSlots>;
|
|
37
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
38
|
+
new (): {
|
|
39
|
+
$slots: S;
|
|
40
|
+
};
|
|
41
|
+
};
|