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,115 @@
1
+ import { defineEventHandler, readBody, createError, getRequestIP } from "h3";
2
+ import { useRuntimeConfig } from "#imports";
3
+ import { checkRateLimit } from "../utils/feedback-rate-limit.js";
4
+ import { verifyCaptcha } from "../utils/feedback-captcha.js";
5
+ function escapeHtml(str) {
6
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
7
+ }
8
+ export default defineEventHandler(async (event) => {
9
+ const config = useRuntimeConfig(event);
10
+ const feedbackConfig = config.uiElementsProFeedback;
11
+ if (!feedbackConfig) {
12
+ throw createError({ statusCode: 500, message: "Feedback not configured" });
13
+ }
14
+ const body = await readBody(event);
15
+ if (!body) {
16
+ throw createError({ statusCode: 400, message: "Request body is required" });
17
+ }
18
+ if (body._hp) {
19
+ return { success: true };
20
+ }
21
+ const ip = getRequestIP(event, { xForwardedFor: true }) ?? "unknown";
22
+ const maxRequests = feedbackConfig.rateLimitMax ?? 5;
23
+ const windowSeconds = feedbackConfig.rateLimitWindow ?? 3600;
24
+ const rateLimitResult = checkRateLimit(ip, maxRequests, windowSeconds);
25
+ if (!rateLimitResult.allowed) {
26
+ throw createError({
27
+ statusCode: 429,
28
+ message: `Rate limit exceeded. Try again in ${rateLimitResult.retryAfter} seconds.`
29
+ });
30
+ }
31
+ if (feedbackConfig.captchaSecretKey && feedbackConfig.captchaProvider) {
32
+ if (!body.captchaToken) {
33
+ throw createError({ statusCode: 400, message: "CAPTCHA token required" });
34
+ }
35
+ const captchaValid = await verifyCaptcha(
36
+ feedbackConfig.captchaProvider,
37
+ feedbackConfig.captchaSecretKey,
38
+ body.captchaToken,
39
+ ip
40
+ );
41
+ if (!captchaValid) {
42
+ throw createError({ statusCode: 400, message: "CAPTCHA verification failed" });
43
+ }
44
+ }
45
+ if (!body.type || !body.email?.trim() || !body.message?.trim()) {
46
+ throw createError({ statusCode: 400, message: "Missing required fields" });
47
+ }
48
+ const payload = {
49
+ type: body.type,
50
+ email: body.email.trim(),
51
+ message: body.message.trim(),
52
+ screenshot: body.screenshot,
53
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
54
+ };
55
+ if (feedbackConfig.webhook) {
56
+ try {
57
+ await $fetch(feedbackConfig.webhook, {
58
+ method: "POST",
59
+ body: payload
60
+ });
61
+ } catch {
62
+ throw createError({
63
+ statusCode: 502,
64
+ message: "Failed to forward feedback to webhook"
65
+ });
66
+ }
67
+ }
68
+ if (feedbackConfig.emailTo && feedbackConfig.smtpHost) {
69
+ try {
70
+ const nodemailer = await import("nodemailer");
71
+ const transporter = nodemailer.createTransport({
72
+ host: feedbackConfig.smtpHost,
73
+ port: feedbackConfig.smtpPort ?? 587,
74
+ secure: feedbackConfig.smtpSecure ?? false,
75
+ ...feedbackConfig.smtpUser ? {
76
+ auth: {
77
+ user: feedbackConfig.smtpUser,
78
+ pass: feedbackConfig.smtpPass
79
+ }
80
+ } : {}
81
+ });
82
+ const safeType = escapeHtml(payload.type);
83
+ const safeEmail = escapeHtml(payload.email);
84
+ const safeMessage = escapeHtml(payload.message).replace(/\n/g, "<br>");
85
+ await transporter.sendMail({
86
+ from: feedbackConfig.emailFrom ?? feedbackConfig.emailTo,
87
+ to: feedbackConfig.emailTo,
88
+ subject: `[Feedback] ${safeType}: from ${safeEmail}`,
89
+ html: [
90
+ `<h2>New Feedback (${safeType})</h2>`,
91
+ `<p><strong>From:</strong> ${safeEmail}</p>`,
92
+ `<p><strong>Type:</strong> ${safeType}</p>`,
93
+ `<p><strong>Message:</strong></p>`,
94
+ `<p>${safeMessage}</p>`,
95
+ payload.screenshot ? `<p><strong>Screenshot attached</strong></p>` : ""
96
+ ].join("\n"),
97
+ ...payload.screenshot ? {
98
+ attachments: [
99
+ {
100
+ filename: "screenshot.png",
101
+ content: payload.screenshot.replace(/^data:image\/png;base64,/, ""),
102
+ encoding: "base64"
103
+ }
104
+ ]
105
+ } : {}
106
+ });
107
+ } catch {
108
+ throw createError({
109
+ statusCode: 500,
110
+ message: "Failed to send feedback email"
111
+ });
112
+ }
113
+ }
114
+ return { success: true };
115
+ });
@@ -0,0 +1,29 @@
1
+ declare module "nodemailer" {
2
+ interface TransportOptions {
3
+ host?: string
4
+ port?: number
5
+ secure?: boolean
6
+ auth?: {
7
+ user?: string
8
+ pass?: string
9
+ }
10
+ }
11
+
12
+ interface MailOptions {
13
+ from?: string
14
+ to?: string
15
+ subject?: string
16
+ html?: string
17
+ attachments?: Array<{
18
+ filename?: string
19
+ content?: string
20
+ encoding?: string
21
+ }>
22
+ }
23
+
24
+ interface Transporter {
25
+ sendMail(options: MailOptions): Promise<any>
26
+ }
27
+
28
+ function createTransport(options: TransportOptions): Transporter
29
+ }
@@ -0,0 +1 @@
1
+ export declare function verifyCaptcha(provider: string, secretKey: string, token: string, ip?: string): Promise<boolean>;
@@ -0,0 +1,27 @@
1
+ const VERIFY_URLS = {
2
+ turnstile: "https://challenges.cloudflare.com/turnstile/v0/siteverify",
3
+ recaptcha: "https://www.google.com/recaptcha/api/siteverify",
4
+ hcaptcha: "https://api.hcaptcha.com/siteverify"
5
+ };
6
+ export async function verifyCaptcha(provider, secretKey, token, ip) {
7
+ const url = VERIFY_URLS[provider];
8
+ if (!url) {
9
+ console.warn(`[FeedbackWidget] Unknown CAPTCHA provider: ${provider}`);
10
+ return false;
11
+ }
12
+ try {
13
+ const params = {
14
+ secret: secretKey,
15
+ response: token
16
+ };
17
+ if (ip) params.remoteip = ip;
18
+ const result = await $fetch(url, {
19
+ method: "POST",
20
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
21
+ body: new URLSearchParams(params).toString()
22
+ });
23
+ return result.success === true;
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
@@ -0,0 +1,4 @@
1
+ export declare function checkRateLimit(key: string, max: number, windowSeconds: number): {
2
+ allowed: boolean;
3
+ retryAfter?: number;
4
+ };
@@ -0,0 +1,26 @@
1
+ const store = /* @__PURE__ */ new Map();
2
+ const cleanupTimer = setInterval(() => {
3
+ const now = Date.now();
4
+ for (const [key, entry] of store) {
5
+ entry.timestamps = entry.timestamps.filter((t) => now - t < 72e5);
6
+ if (entry.timestamps.length === 0) store.delete(key);
7
+ }
8
+ }, 6e5);
9
+ cleanupTimer.unref();
10
+ export function checkRateLimit(key, max, windowSeconds) {
11
+ const now = Date.now();
12
+ const windowMs = windowSeconds * 1e3;
13
+ let entry = store.get(key);
14
+ if (!entry) {
15
+ entry = { timestamps: [] };
16
+ store.set(key, entry);
17
+ }
18
+ entry.timestamps = entry.timestamps.filter((t) => now - t < windowMs);
19
+ if (entry.timestamps.length >= max) {
20
+ const oldestInWindow = entry.timestamps[0];
21
+ const retryAfter = Math.ceil((oldestInWindow + windowMs - now) / 1e3);
22
+ return { allowed: false, retryAfter };
23
+ }
24
+ entry.timestamps.push(now);
25
+ return { allowed: true };
26
+ }
@@ -73,21 +73,25 @@ export interface MonthViewOptions {
73
73
  }
74
74
  /** Week view configuration */
75
75
  export interface WeekViewOptions {
76
- /** First visible hour (0-23) @defaultValue 7 */
76
+ /** First visible hour (0-23) @defaultValue 0 */
77
77
  startHour?: number;
78
- /** Last visible hour (0-23) @defaultValue 22 */
78
+ /** Last visible hour (0-24) @defaultValue 24 */
79
79
  endHour?: number;
80
80
  /** Minutes per time slot row @defaultValue 30 */
81
81
  slotDuration?: 15 | 30 | 60;
82
+ /** Hour to scroll to on initial render (0-23) @defaultValue 7 */
83
+ scrollTime?: number;
82
84
  }
83
85
  /** Day view configuration */
84
86
  export interface DayViewOptions {
85
- /** First visible hour (0-23) @defaultValue 7 */
87
+ /** First visible hour (0-23) @defaultValue 0 */
86
88
  startHour?: number;
87
- /** Last visible hour (0-23) @defaultValue 22 */
89
+ /** Last visible hour (0-24) @defaultValue 24 */
88
90
  endHour?: number;
89
91
  /** Minutes per time slot row @defaultValue 30 */
90
92
  slotDuration?: 15 | 30 | 60;
93
+ /** Hour to scroll to on initial render (0-23) @defaultValue 7 */
94
+ scrollTime?: number;
91
95
  }
92
96
  /** List/agenda view configuration */
93
97
  export interface ListViewOptions {
@@ -201,11 +205,13 @@ export interface EventCalendarContext {
201
205
  startHour: number;
202
206
  endHour: number;
203
207
  slotDuration: number;
208
+ scrollTime: number;
204
209
  }>;
205
210
  dayConfig: ComputedRef<{
206
211
  startHour: number;
207
212
  endHour: number;
208
213
  slotDuration: number;
214
+ scrollTime: number;
209
215
  }>;
210
216
  monthWeeks: ComputedRef<CalendarWeek[]>;
211
217
  monthWeekLayouts: ComputedRef<MonthWeekLayout[]>;
@@ -0,0 +1,110 @@
1
+ import type { ComputedRef, Ref } from "vue";
2
+ /** Built-in feedback type identifiers */
3
+ export type FeedbackTypeId = "bug" | "feature" | "general" | (string & {});
4
+ /** A feedback type option shown in the type selector */
5
+ export interface FeedbackType {
6
+ /** Unique identifier */
7
+ id: FeedbackTypeId;
8
+ /** Display label */
9
+ label: string;
10
+ /** Icon name (e.g., 'i-lucide-bug') */
11
+ icon?: string;
12
+ /** Description shown below label */
13
+ description?: string;
14
+ }
15
+ /** The data submitted in a feedback form */
16
+ export interface FeedbackSubmission {
17
+ /** Feedback type ID */
18
+ type: FeedbackTypeId;
19
+ /** User email */
20
+ email: string;
21
+ /** Feedback message body */
22
+ message: string;
23
+ /** Optional screenshot as base64 data URL */
24
+ screenshot?: string;
25
+ /** Honeypot field value (should be empty for real users) */
26
+ _hp?: string;
27
+ /** CAPTCHA token (if captcha is enabled) */
28
+ captchaToken?: string;
29
+ }
30
+ /** Result from submission attempt */
31
+ export interface FeedbackSubmitResult {
32
+ /** Whether submission succeeded */
33
+ success: boolean;
34
+ /** Optional error message */
35
+ error?: string;
36
+ }
37
+ /** Widget position on the screen */
38
+ export type FeedbackWidgetPosition = "bottom-right" | "bottom-left" | "top-right" | "top-left";
39
+ export type CaptchaProvider = "turnstile" | "recaptcha" | "hcaptcha";
40
+ export interface CaptchaConfig {
41
+ /** CAPTCHA provider */
42
+ provider: CaptchaProvider;
43
+ /** Public site key (exposed to client) */
44
+ siteKey: string;
45
+ /** Secret key (server-side only, never sent to client) */
46
+ secretKey: string;
47
+ }
48
+ export interface RateLimitConfig {
49
+ /** Maximum requests per window @defaultValue 5 */
50
+ max: number;
51
+ /** Window duration in seconds @defaultValue 3600 (1 hour) */
52
+ window: number;
53
+ }
54
+ export interface FeedbackEmailConfig {
55
+ /** Recipient email */
56
+ to: string;
57
+ /** Sender email */
58
+ from: string;
59
+ /** SMTP connection settings */
60
+ smtp: {
61
+ host: string;
62
+ port: number;
63
+ secure?: boolean;
64
+ auth?: {
65
+ user: string;
66
+ pass: string;
67
+ };
68
+ };
69
+ }
70
+ export interface FeedbackModuleConfig {
71
+ /** Webhook URL to POST feedback JSON to */
72
+ webhook?: string;
73
+ /** Email delivery configuration */
74
+ email?: FeedbackEmailConfig;
75
+ /** CAPTCHA configuration */
76
+ captcha?: CaptchaConfig;
77
+ /** Rate limiting @defaultValue { max: 5, window: 3600 } */
78
+ rateLimit?: Partial<RateLimitConfig>;
79
+ }
80
+ export interface FeedbackWidgetContext {
81
+ isOpen: Ref<boolean>;
82
+ isSubmitting: Ref<boolean>;
83
+ isSubmitted: Ref<boolean>;
84
+ submitError: Ref<string | null>;
85
+ selectedType: Ref<FeedbackTypeId | null>;
86
+ email: Ref<string>;
87
+ message: Ref<string>;
88
+ screenshot: Ref<string | null>;
89
+ honeypot: Ref<string>;
90
+ feedbackTypes: ComputedRef<FeedbackType[]>;
91
+ position: ComputedRef<FeedbackWidgetPosition>;
92
+ showScreenshot: ComputedRef<boolean>;
93
+ emailPlaceholder: ComputedRef<string>;
94
+ messagePlaceholder: ComputedRef<string>;
95
+ captchaEnabled: ComputedRef<boolean>;
96
+ captchaProvider: ComputedRef<CaptchaProvider | null>;
97
+ captchaSiteKey: ComputedRef<string | null>;
98
+ open: () => void;
99
+ close: () => void;
100
+ toggle: () => void;
101
+ reset: () => void;
102
+ submit: () => Promise<void>;
103
+ captureScreenshot: () => Promise<void>;
104
+ removeScreenshot: () => void;
105
+ setCaptchaToken: (token: string) => void;
106
+ isValid: ComputedRef<boolean>;
107
+ color: ComputedRef<string>;
108
+ ui: ComputedRef<any>;
109
+ propUi: ComputedRef<any>;
110
+ }
File without changes
@@ -0,0 +1,223 @@
1
+ import type { CalendarDate, DateValue } from "@internationalized/date";
2
+ import type { ComputedRef, Ref } from "vue";
3
+ /** All supported Gantt chart zoom levels */
4
+ export type GanttZoomLevel = "day" | "week" | "month" | "quarter" | "year";
5
+ /**
6
+ * A single task in the Gantt chart.
7
+ * Accepts flexible date inputs (Date, ISO string, or DateValue).
8
+ */
9
+ export interface GanttTask {
10
+ /** Unique identifier */
11
+ id: string | number;
12
+ /** Display title */
13
+ title: string;
14
+ /** Task start date */
15
+ start: Date | string | DateValue;
16
+ /** Task end date (inclusive) */
17
+ end: Date | string | DateValue;
18
+ /** Parent task ID for hierarchy. Omit or set to null for top-level tasks. */
19
+ parentId?: string | number | null;
20
+ /** Nuxt UI color name (e.g., 'primary', 'success') */
21
+ color?: string;
22
+ /** Progress percentage 0-100 @defaultValue 0 */
23
+ progress?: number;
24
+ /** Whether this is a milestone (zero-duration marker). @defaultValue false */
25
+ milestone?: boolean;
26
+ /** Whether this task can be dragged. @defaultValue true */
27
+ draggable?: boolean;
28
+ /** Whether this task can be resized. @defaultValue true */
29
+ resizable?: boolean;
30
+ /** Arbitrary user-defined metadata passed through to slots */
31
+ metadata?: Record<string, unknown>;
32
+ }
33
+ /** A dependency link between two tasks */
34
+ export interface GanttDependency {
35
+ /** Unique identifier for this dependency */
36
+ id: string | number;
37
+ /** Source task ID (predecessor — must finish before successor starts) */
38
+ fromId: string | number;
39
+ /** Target task ID (successor) */
40
+ toId: string | number;
41
+ /** Dependency type. @defaultValue 'finish-to-start' */
42
+ type?: "finish-to-start";
43
+ }
44
+ /** Internal normalized task with parsed CalendarDates */
45
+ export interface NormalizedGanttTask {
46
+ id: string | number;
47
+ title: string;
48
+ start: CalendarDate;
49
+ end: CalendarDate;
50
+ parentId: string | number | null;
51
+ color: string;
52
+ progress: number;
53
+ milestone: boolean;
54
+ draggable: boolean;
55
+ resizable: boolean;
56
+ metadata: Record<string, unknown>;
57
+ original: GanttTask;
58
+ }
59
+ /** Internal tree node for hierarchical task groups */
60
+ export interface GanttTaskTreeNode {
61
+ /** The normalized task data */
62
+ task: NormalizedGanttTask;
63
+ /** Direct children */
64
+ children: GanttTaskTreeNode[];
65
+ /** Depth in the tree (root-level = 0) */
66
+ depth: number;
67
+ }
68
+ /** Computed bar position for a single task */
69
+ export interface GanttTaskBar {
70
+ /** The normalized task */
71
+ task: NormalizedGanttTask;
72
+ /** Row index in the flattened visible list */
73
+ rowIndex: number;
74
+ /** Depth (indent level) */
75
+ depth: number;
76
+ /** Is this a group (has children)? */
77
+ isGroup: boolean;
78
+ /** Is this group expanded? (only relevant if isGroup) */
79
+ isExpanded: boolean;
80
+ /** Number of direct children */
81
+ childCount: number;
82
+ /** Left offset in pixels from timeline start */
83
+ leftPx: number;
84
+ /** Width of the bar in pixels */
85
+ widthPx: number;
86
+ /** Whether this bar is a milestone diamond */
87
+ isMilestone: boolean;
88
+ }
89
+ /** A dependency arrow path */
90
+ export interface GanttDependencyPath {
91
+ /** Original dependency data */
92
+ dependency: GanttDependency;
93
+ /** SVG path data string (d attribute) */
94
+ path: string;
95
+ /** Source task bar */
96
+ from: GanttTaskBar;
97
+ /** Target task bar */
98
+ to: GanttTaskBar;
99
+ /** Connection point on the source bar (right edge center) */
100
+ fromPoint: {
101
+ x: number;
102
+ y: number;
103
+ };
104
+ /** Connection point on the target bar (left edge center) */
105
+ toPoint: {
106
+ x: number;
107
+ y: number;
108
+ };
109
+ }
110
+ /** A column in the timeline header */
111
+ export interface GanttTimelineColumn {
112
+ /** The date this column represents */
113
+ date: CalendarDate;
114
+ /** Display label for this column */
115
+ label: string;
116
+ /** Width in pixels */
117
+ widthPx: number;
118
+ /** Left offset in pixels */
119
+ leftPx: number;
120
+ /** Is today */
121
+ isToday: boolean;
122
+ /** Is weekend (Saturday or Sunday) */
123
+ isWeekend: boolean;
124
+ }
125
+ /** A header group (top-level grouping above columns) */
126
+ export interface GanttHeaderGroup {
127
+ /** Display label (e.g., "January 2026", "Q1 2026") */
128
+ label: string;
129
+ /** Number of sub-columns this group spans */
130
+ span: number;
131
+ /** Left offset in pixels */
132
+ leftPx: number;
133
+ /** Total width in pixels */
134
+ widthPx: number;
135
+ }
136
+ /** Full layout result */
137
+ export interface GanttLayout {
138
+ /** All visible task bars with positions */
139
+ taskBars: GanttTaskBar[];
140
+ /** All dependency arrow paths */
141
+ dependencyPaths: GanttDependencyPath[];
142
+ /** Timeline columns (bottom header row) */
143
+ columns: GanttTimelineColumn[];
144
+ /** Header groups (top header row) */
145
+ headerGroups: GanttHeaderGroup[];
146
+ /** Total timeline width in pixels */
147
+ totalWidth: number;
148
+ /** Total height in pixels (rows * rowHeight) */
149
+ totalHeight: number;
150
+ /** Today marker left offset in pixels, or null if not in view */
151
+ todayOffsetPx: number | null;
152
+ }
153
+ /** Per-zoom-level configuration */
154
+ export interface GanttZoomConfig {
155
+ /** Pixel width per time unit at this zoom level */
156
+ unitWidth: number;
157
+ }
158
+ /** Resolved zoom presets (one entry per zoom level) */
159
+ export type GanttZoomPresets = Record<GanttZoomLevel, GanttZoomConfig>;
160
+ /** Payload emitted when a task is moved via drag */
161
+ export interface GanttTaskDropPayload {
162
+ task: GanttTask;
163
+ oldStart: CalendarDate;
164
+ oldEnd: CalendarDate;
165
+ newStart: CalendarDate;
166
+ newEnd: CalendarDate;
167
+ }
168
+ /** Payload emitted when a task is resized */
169
+ export interface GanttTaskResizePayload {
170
+ task: GanttTask;
171
+ oldStart: CalendarDate;
172
+ oldEnd: CalendarDate;
173
+ newStart: CalendarDate;
174
+ newEnd: CalendarDate;
175
+ /** Which handle was dragged */
176
+ edge: "start" | "end";
177
+ }
178
+ /** The context provided by GanttChart root to sub-components via provide/inject */
179
+ export interface GanttChartContext {
180
+ zoomLevel: ComputedRef<GanttZoomLevel>;
181
+ locale: ComputedRef<string>;
182
+ editable: ComputedRef<boolean>;
183
+ loading: ComputedRef<boolean>;
184
+ color: ComputedRef<string>;
185
+ layout: ComputedRef<GanttLayout>;
186
+ rowHeight: ComputedRef<number>;
187
+ timelineStart: ComputedRef<CalendarDate>;
188
+ timelineEnd: ComputedRef<CalendarDate>;
189
+ headerTitle: ComputedRef<string>;
190
+ goToPrev: () => void;
191
+ goToNext: () => void;
192
+ goToToday: () => void;
193
+ setZoomLevel: (level: GanttZoomLevel) => void;
194
+ scrollToDate: (date: CalendarDate) => void;
195
+ scrollToTask: (taskId: string | number) => void;
196
+ zoomLevels: ComputedRef<GanttZoomLevel[]>;
197
+ expandedIds: Ref<Set<string | number>>;
198
+ toggleExpand: (id: string | number) => void;
199
+ expandAll: () => void;
200
+ collapseAll: () => void;
201
+ isExpanded: (id: string | number) => boolean;
202
+ handleTaskClick: (task: NormalizedGanttTask, e: Event) => void;
203
+ getTaskStyle: (task: NormalizedGanttTask) => Record<string, string>;
204
+ draggedTaskId: Ref<string | number | null>;
205
+ dragPreview: Ref<{
206
+ leftPx: number;
207
+ widthPx: number;
208
+ } | null>;
209
+ onDragStart: (bar: GanttTaskBar, e: PointerEvent) => void;
210
+ resizingTaskId: Ref<string | number | null>;
211
+ resizeEdge: Ref<"start" | "end" | null>;
212
+ resizePreview: Ref<{
213
+ leftPx: number;
214
+ widthPx: number;
215
+ } | null>;
216
+ onResizePointerDown: (bar: GanttTaskBar, edge: "start" | "end", e: PointerEvent) => void;
217
+ focusedTaskId: Ref<string | number | null>;
218
+ onKeydown: (e: KeyboardEvent) => void;
219
+ dateToPixel: (date: CalendarDate) => number;
220
+ pixelToDate: (px: number) => CalendarDate;
221
+ ui: ComputedRef<any>;
222
+ propUi: ComputedRef<any>;
223
+ }
File without changes
@@ -2,3 +2,7 @@ export * from "./event-calendar.js";
2
2
  export * from "../components/EventCalendar.vue.js";
3
3
  export * from "./org-chart.js";
4
4
  export * from "../components/OrgChart.vue.js";
5
+ export * from "./feedback-widget.js";
6
+ export * from "../components/FeedbackWidget.vue.js";
7
+ export * from "./gantt-chart.js";
8
+ export * from "../components/GanttChart.vue.js";
@@ -2,3 +2,7 @@ export * from "./event-calendar.js";
2
2
  export * from "../components/EventCalendar.vue";
3
3
  export * from "./org-chart.js";
4
4
  export * from "../components/OrgChart.vue";
5
+ export * from "./feedback-widget.js";
6
+ export * from "../components/FeedbackWidget.vue";
7
+ export * from "./gantt-chart.js";
8
+ export * from "../components/GanttChart.vue";
@@ -0,0 +1,15 @@
1
+ import type { InjectionKey } from "vue";
2
+ /**
3
+ * Creates a typed provide/inject context pair for compound components.
4
+ *
5
+ * @param componentName - Display name used in the Symbol key and error messages
6
+ * (e.g. "EventCalendar", "FeedbackWidget").
7
+ * @returns An object with:
8
+ * - `key`: An `InjectionKey<T>` symbol for `provide()` / `inject()`.
9
+ * - `useContext`: A function that injects the context value and throws a
10
+ * descriptive error when called outside the root component.
11
+ */
12
+ export declare function createComponentContext<T>(componentName: string): {
13
+ key: InjectionKey<T>;
14
+ useContext: () => T;
15
+ };
@@ -0,0 +1,17 @@
1
+ import { inject } from "vue";
2
+ export function createComponentContext(componentName) {
3
+ const key = Symbol(`nuxt-ui-elements-pro.${kebab(componentName)}`);
4
+ function useContext() {
5
+ const ctx = inject(key);
6
+ if (!ctx) {
7
+ throw new Error(
8
+ `[${componentName}] use${componentName}Context() was called outside of a <UE${componentName}> root. Sub-components must be used as children of <UE${componentName}>.`
9
+ );
10
+ }
11
+ return ctx;
12
+ }
13
+ return { key, useContext };
14
+ }
15
+ function kebab(str) {
16
+ return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
17
+ }
@@ -0,0 +1,10 @@
1
+ import type { CalendarDate } from "@internationalized/date";
2
+ /**
3
+ * Compute the start of the week for any weekStartsOn value (0=Sunday … 6=Saturday).
4
+ * Uses getDayOfWeek with a fixed locale ('en-US') so Sunday is always 0.
5
+ */
6
+ export declare function startOfWeekByDay(date: CalendarDate, weekStartsOn: number): CalendarDate;
7
+ /**
8
+ * Compute the end of the week for any weekStartsOn value.
9
+ */
10
+ export declare function endOfWeekByDay(date: CalendarDate, weekStartsOn: number): CalendarDate;
@@ -0,0 +1,9 @@
1
+ import { getDayOfWeek, toCalendarDate } from "@internationalized/date";
2
+ export function startOfWeekByDay(date, weekStartsOn) {
3
+ const dow = getDayOfWeek(date, "en-US");
4
+ const diff = (dow - weekStartsOn + 7) % 7;
5
+ return toCalendarDate(date.subtract({ days: diff }));
6
+ }
7
+ export function endOfWeekByDay(date, weekStartsOn) {
8
+ return toCalendarDate(startOfWeekByDay(date, weekStartsOn).add({ days: 6 }));
9
+ }