@pichetch08/trip-ui 0.1.0

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 (145) hide show
  1. package/README.md +46 -0
  2. package/dist/accordion.d.ts +18 -0
  3. package/dist/accordion.js +76 -0
  4. package/dist/agreement-modal.d.ts +16 -0
  5. package/dist/agreement-modal.js +67 -0
  6. package/dist/alert.d.ts +13 -0
  7. package/dist/alert.js +47 -0
  8. package/dist/auth-hero.d.ts +7 -0
  9. package/dist/auth-hero.js +63 -0
  10. package/dist/avatar.d.ts +21 -0
  11. package/dist/avatar.js +114 -0
  12. package/dist/badge.d.ts +13 -0
  13. package/dist/badge.js +36 -0
  14. package/dist/banner.d.ts +14 -0
  15. package/dist/banner.js +57 -0
  16. package/dist/breadcrumb.d.ts +15 -0
  17. package/dist/breadcrumb.js +37 -0
  18. package/dist/button.d.ts +15 -0
  19. package/dist/button.js +81 -0
  20. package/dist/card.d.ts +30 -0
  21. package/dist/card.js +66 -0
  22. package/dist/change-summary-modal.d.ts +35 -0
  23. package/dist/change-summary-modal.js +128 -0
  24. package/dist/channel-badge.d.ts +8 -0
  25. package/dist/channel-badge.js +17 -0
  26. package/dist/checkbox.d.ts +28 -0
  27. package/dist/checkbox.js +108 -0
  28. package/dist/chunk-ORMEWXMH.js +37 -0
  29. package/dist/color-picker.d.ts +15 -0
  30. package/dist/color-picker.js +159 -0
  31. package/dist/confirm-dialog.d.ts +23 -0
  32. package/dist/confirm-dialog.js +108 -0
  33. package/dist/copy-button.d.ts +13 -0
  34. package/dist/copy-button.js +69 -0
  35. package/dist/dashed-add-button.d.ts +8 -0
  36. package/dist/dashed-add-button.js +24 -0
  37. package/dist/data-table.d.ts +27 -0
  38. package/dist/data-table.js +152 -0
  39. package/dist/date-picker.d.ts +19 -0
  40. package/dist/date-picker.js +234 -0
  41. package/dist/date-range-picker.d.ts +25 -0
  42. package/dist/date-range-picker.js +456 -0
  43. package/dist/dev-auto-fill.d.ts +12 -0
  44. package/dist/dev-auto-fill.js +22 -0
  45. package/dist/divider.d.ts +10 -0
  46. package/dist/divider.js +44 -0
  47. package/dist/drawer.d.ts +16 -0
  48. package/dist/drawer.js +111 -0
  49. package/dist/dropdown-menu.d.ts +20 -0
  50. package/dist/dropdown-menu.js +94 -0
  51. package/dist/empty-state.d.ts +13 -0
  52. package/dist/empty-state.js +24 -0
  53. package/dist/file-upload.d.ts +32 -0
  54. package/dist/file-upload.js +212 -0
  55. package/dist/filter-tabs.d.ts +16 -0
  56. package/dist/filter-tabs.js +30 -0
  57. package/dist/footer-action-bar.d.ts +21 -0
  58. package/dist/footer-action-bar.js +95 -0
  59. package/dist/form-input.d.ts +16 -0
  60. package/dist/form-input.js +58 -0
  61. package/dist/form-textarea.d.ts +13 -0
  62. package/dist/form-textarea.js +41 -0
  63. package/dist/icon-button.d.ts +12 -0
  64. package/dist/icon-button.js +54 -0
  65. package/dist/icon-picker.d.ts +15 -0
  66. package/dist/icon-picker.js +311 -0
  67. package/dist/icon-wrapper.d.ts +15 -0
  68. package/dist/icon-wrapper.js +52 -0
  69. package/dist/image-upload.d.ts +24 -0
  70. package/dist/image-upload.js +122 -0
  71. package/dist/index.d.ts +71 -0
  72. package/dist/index.js +155 -0
  73. package/dist/kbd.d.ts +15 -0
  74. package/dist/kbd.js +27 -0
  75. package/dist/mobile-preview.d.ts +36 -0
  76. package/dist/mobile-preview.js +167 -0
  77. package/dist/modal.d.ts +19 -0
  78. package/dist/modal.js +110 -0
  79. package/dist/multi-select.d.ts +30 -0
  80. package/dist/multi-select.js +261 -0
  81. package/dist/number-input.d.ts +21 -0
  82. package/dist/number-input.js +129 -0
  83. package/dist/otp-input.d.ts +13 -0
  84. package/dist/otp-input.js +114 -0
  85. package/dist/page-header.d.ts +15 -0
  86. package/dist/page-header.js +43 -0
  87. package/dist/page-state.d.ts +14 -0
  88. package/dist/page-state.js +29 -0
  89. package/dist/pagination.d.ts +20 -0
  90. package/dist/pagination.js +87 -0
  91. package/dist/popover.d.ts +11 -0
  92. package/dist/popover.js +70 -0
  93. package/dist/preview-drawer.d.ts +33 -0
  94. package/dist/preview-drawer.js +74 -0
  95. package/dist/progress-bar.d.ts +15 -0
  96. package/dist/progress-bar.js +56 -0
  97. package/dist/qr-code-display.d.ts +10 -0
  98. package/dist/qr-code-display.js +43 -0
  99. package/dist/radio-group.d.ts +19 -0
  100. package/dist/radio-group.js +78 -0
  101. package/dist/rating.d.ts +12 -0
  102. package/dist/rating.js +123 -0
  103. package/dist/rich-editor.d.ts +13 -0
  104. package/dist/rich-editor.js +97 -0
  105. package/dist/search-bar.d.ts +14 -0
  106. package/dist/search-bar.js +64 -0
  107. package/dist/section-header.d.ts +12 -0
  108. package/dist/section-header.js +41 -0
  109. package/dist/segmented-control.d.ts +24 -0
  110. package/dist/segmented-control.js +38 -0
  111. package/dist/select-picker.d.ts +24 -0
  112. package/dist/select-picker.js +157 -0
  113. package/dist/skeleton.d.ts +14 -0
  114. package/dist/skeleton.js +53 -0
  115. package/dist/slider.d.ts +17 -0
  116. package/dist/slider.js +151 -0
  117. package/dist/spinner.d.ts +13 -0
  118. package/dist/spinner.js +38 -0
  119. package/dist/stat-card.d.ts +20 -0
  120. package/dist/stat-card.js +87 -0
  121. package/dist/stats-summary.d.ts +13 -0
  122. package/dist/stats-summary.js +28 -0
  123. package/dist/status-badge.d.ts +19 -0
  124. package/dist/status-badge.js +41 -0
  125. package/dist/stepper.d.ts +12 -0
  126. package/dist/stepper.js +89 -0
  127. package/dist/tabs.d.ts +18 -0
  128. package/dist/tabs.js +70 -0
  129. package/dist/tag.d.ts +23 -0
  130. package/dist/tag.js +158 -0
  131. package/dist/time-picker.d.ts +19 -0
  132. package/dist/time-picker.js +222 -0
  133. package/dist/timeline.d.ts +15 -0
  134. package/dist/timeline.js +49 -0
  135. package/dist/toast.d.ts +18 -0
  136. package/dist/toast.js +108 -0
  137. package/dist/toggle-switch.d.ts +12 -0
  138. package/dist/toggle-switch.js +34 -0
  139. package/dist/tooltip.d.ts +9 -0
  140. package/dist/tooltip.js +69 -0
  141. package/dist/trip-day-map-lazy.d.ts +15 -0
  142. package/dist/trip-day-map-lazy.js +16 -0
  143. package/dist/trip-day-map.d.ts +15 -0
  144. package/dist/trip-day-map.js +62 -0
  145. package/package.json +73 -0
@@ -0,0 +1,87 @@
1
+ "use client";
2
+ import {
3
+ __spreadValues
4
+ } from "./chunk-ORMEWXMH.js";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ const defaultLabels = {
7
+ showing: (from, to, total) => `\u0E41\u0E2A\u0E14\u0E07 ${from}\u2013${to} \u0E08\u0E32\u0E01 ${total} \u0E23\u0E32\u0E22\u0E01\u0E32\u0E23`,
8
+ page: (current, total) => `\u0E2B\u0E19\u0E49\u0E32 ${current} \u0E08\u0E32\u0E01 ${total}`,
9
+ prevPage: "\u0E2B\u0E19\u0E49\u0E32\u0E01\u0E48\u0E2D\u0E19\u0E2B\u0E19\u0E49\u0E32",
10
+ nextPage: "\u0E2B\u0E19\u0E49\u0E32\u0E16\u0E31\u0E14\u0E44\u0E1B",
11
+ pageLabel: (page) => `\u0E2B\u0E19\u0E49\u0E32 ${page}`,
12
+ nav: "\u0E01\u0E32\u0E23\u0E41\u0E1A\u0E48\u0E07\u0E2B\u0E19\u0E49\u0E32"
13
+ };
14
+ function Pagination({
15
+ currentPage,
16
+ totalPages,
17
+ onPageChange,
18
+ totalItems,
19
+ pageSize,
20
+ labels = {}
21
+ }) {
22
+ const L = __spreadValues(__spreadValues({}, defaultLabels), labels);
23
+ const pages = [];
24
+ const maxVisible = 5;
25
+ let start = Math.max(1, currentPage - 2);
26
+ let end = Math.min(totalPages, start + maxVisible - 1);
27
+ if (end - start < maxVisible - 1) start = Math.max(1, end - maxVisible + 1);
28
+ if (start > 1) {
29
+ pages.push(1);
30
+ if (start > 2) pages.push(null);
31
+ }
32
+ for (let i = start; i <= end; i++) pages.push(i);
33
+ if (end < totalPages) {
34
+ if (end < totalPages - 1) pages.push(null);
35
+ pages.push(totalPages);
36
+ }
37
+ const showCount = totalItems != null && pageSize != null;
38
+ const from = showCount ? (currentPage - 1) * pageSize + 1 : null;
39
+ const to = showCount ? Math.min(currentPage * pageSize, totalItems) : null;
40
+ return /* @__PURE__ */ jsxs("nav", { className: "flex items-center justify-between px-1", "aria-label": L.nav, children: [
41
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-on-surface-variant", "aria-live": "polite", "aria-atomic": "true", children: showCount ? L.showing(from, to, totalItems) : L.page(currentPage, totalPages) }),
42
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
43
+ /* @__PURE__ */ jsx(
44
+ "button",
45
+ {
46
+ type: "button",
47
+ onClick: () => onPageChange(currentPage - 1),
48
+ disabled: currentPage === 1,
49
+ "aria-label": L.prevPage,
50
+ className: "p-2 rounded-lg border border-outline-variant/20 hover:bg-surface-container-low disabled:opacity-50 disabled:cursor-not-allowed transition-colors active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30",
51
+ children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-base leading-none", "aria-hidden": "true", children: "chevron_left" })
52
+ }
53
+ ),
54
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", role: "list", children: pages.map((page, idx) => {
55
+ if (page === null) {
56
+ return /* @__PURE__ */ jsx("span", { className: "px-2 text-on-surface-variant text-sm", "aria-hidden": "true", children: "\u2026" }, `ellipsis-${idx}`);
57
+ }
58
+ return /* @__PURE__ */ jsx(
59
+ "button",
60
+ {
61
+ type: "button",
62
+ onClick: () => onPageChange(page),
63
+ "aria-label": L.pageLabel(page),
64
+ "aria-current": currentPage === page ? "page" : void 0,
65
+ className: `w-9 h-9 rounded-lg text-sm font-medium transition-colors active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 ${currentPage === page ? "bg-primary text-on-primary" : "border border-outline-variant/20 text-on-surface hover:bg-surface-container-low"}`,
66
+ children: page
67
+ },
68
+ page
69
+ );
70
+ }) }),
71
+ /* @__PURE__ */ jsx(
72
+ "button",
73
+ {
74
+ type: "button",
75
+ onClick: () => onPageChange(currentPage + 1),
76
+ disabled: currentPage === totalPages,
77
+ "aria-label": L.nextPage,
78
+ className: "p-2 rounded-lg border border-outline-variant/20 hover:bg-surface-container-low disabled:opacity-50 disabled:cursor-not-allowed transition-colors active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30",
79
+ children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-base leading-none", "aria-hidden": "true", children: "chevron_right" })
80
+ }
81
+ )
82
+ ] })
83
+ ] });
84
+ }
85
+ export {
86
+ Pagination
87
+ };
@@ -0,0 +1,11 @@
1
+ interface PopoverProps {
2
+ trigger: React.ReactNode;
3
+ children: React.ReactNode;
4
+ placement?: "top" | "bottom" | "left" | "right";
5
+ align?: "start" | "center" | "end";
6
+ className?: string;
7
+ width?: number | "auto";
8
+ }
9
+ declare function Popover({ trigger, children, placement, align, className, width, }: PopoverProps): React.ReactNode;
10
+
11
+ export { Popover };
@@ -0,0 +1,70 @@
1
+ "use client";
2
+ import "./chunk-ORMEWXMH.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { useEffect, useRef, useState } from "react";
5
+ function getPositionClass(placement, align) {
6
+ if (placement === "bottom") {
7
+ const alignClass = align === "start" ? "left-0" : align === "center" ? "left-1/2 -translate-x-1/2" : "right-0";
8
+ return `top-full mt-2 ${alignClass}`;
9
+ }
10
+ if (placement === "top") {
11
+ const alignClass = align === "start" ? "left-0" : align === "center" ? "left-1/2 -translate-x-1/2" : "right-0";
12
+ return `bottom-full mb-2 ${alignClass}`;
13
+ }
14
+ if (placement === "left") {
15
+ return "right-full mr-2 top-0";
16
+ }
17
+ return "left-full ml-2 top-0";
18
+ }
19
+ function Popover({
20
+ trigger,
21
+ children,
22
+ placement = "bottom",
23
+ align = "start",
24
+ className = "",
25
+ width = "auto"
26
+ }) {
27
+ const [open, setOpen] = useState(false);
28
+ const containerRef = useRef(null);
29
+ useEffect(() => {
30
+ if (!open) return;
31
+ const handleMouseDown = (e) => {
32
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
33
+ setOpen(false);
34
+ }
35
+ };
36
+ const handleKeyDown = (e) => {
37
+ if (e.key === "Escape") {
38
+ setOpen(false);
39
+ }
40
+ };
41
+ document.addEventListener("mousedown", handleMouseDown);
42
+ document.addEventListener("keydown", handleKeyDown);
43
+ return () => {
44
+ document.removeEventListener("mousedown", handleMouseDown);
45
+ document.removeEventListener("keydown", handleKeyDown);
46
+ };
47
+ }, [open]);
48
+ const positionClass = getPositionClass(placement, align);
49
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative inline-block", children: [
50
+ /* @__PURE__ */ jsx(
51
+ "div",
52
+ {
53
+ onClick: () => setOpen((v) => !v),
54
+ style: { display: "contents" },
55
+ children: trigger
56
+ }
57
+ ),
58
+ open && /* @__PURE__ */ jsx(
59
+ "div",
60
+ {
61
+ className: `absolute z-50 bg-surface rounded-2xl shadow-xl border border-outline-variant/20 p-4 animate-in fade-in zoom-in-95 duration-150 ${positionClass} ${className}`,
62
+ style: width !== "auto" ? { width } : void 0,
63
+ children
64
+ }
65
+ )
66
+ ] });
67
+ }
68
+ export {
69
+ Popover
70
+ };
@@ -0,0 +1,33 @@
1
+ interface DayLite {
2
+ id: string;
3
+ title: string;
4
+ date: string | null;
5
+ coverImageUrl: string | null;
6
+ activities: {
7
+ id: string;
8
+ time: string | null;
9
+ name: string;
10
+ description: string | null;
11
+ emoji: string | null;
12
+ }[];
13
+ }
14
+ interface PreviewDrawerProps {
15
+ open: boolean;
16
+ onClose: () => void;
17
+ title: string;
18
+ startDate: string;
19
+ endDate: string;
20
+ totalDays: number;
21
+ travelersCount: number;
22
+ coverImageUrl: string | null;
23
+ airlineName: string | null;
24
+ accommodationsCount: number;
25
+ countdownDays: number;
26
+ days: DayLite[];
27
+ drawerTitle?: string;
28
+ closeAriaLabel?: string;
29
+ previewOnlyLabel?: string;
30
+ }
31
+ declare function PreviewDrawer({ open, onClose, drawerTitle, closeAriaLabel, previewOnlyLabel, ...previewProps }: PreviewDrawerProps): React.ReactNode;
32
+
33
+ export { PreviewDrawer };
@@ -0,0 +1,74 @@
1
+ "use client";
2
+ import {
3
+ __objRest,
4
+ __spreadProps,
5
+ __spreadValues
6
+ } from "./chunk-ORMEWXMH.js";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ import { useRef, useState } from "react";
9
+ import { MobilePreview } from "./mobile-preview";
10
+ import { IconButton } from "./icon-button";
11
+ function PreviewDrawer(_a) {
12
+ var _b = _a, {
13
+ open,
14
+ onClose,
15
+ drawerTitle = "\u0E15\u0E31\u0E27\u0E2D\u0E22\u0E48\u0E32\u0E07\u0E1A\u0E19\u0E21\u0E37\u0E2D\u0E16\u0E37\u0E2D",
16
+ closeAriaLabel = "\u0E1B\u0E34\u0E14",
17
+ previewOnlyLabel = "\u0E15\u0E31\u0E27\u0E2D\u0E22\u0E48\u0E32\u0E07\u0E40\u0E17\u0E48\u0E32\u0E19\u0E31\u0E49\u0E19"
18
+ } = _b, previewProps = __objRest(_b, [
19
+ "open",
20
+ "onClose",
21
+ "drawerTitle",
22
+ "closeAriaLabel",
23
+ "previewOnlyLabel"
24
+ ]);
25
+ const [activeDay, setActiveDay] = useState(0);
26
+ const touchStartX = useRef(0);
27
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
28
+ /* @__PURE__ */ jsx(
29
+ "div",
30
+ {
31
+ className: `fixed inset-0 z-49 transition-opacity duration-300 ${open ? "opacity-100 pointer-events-auto" : "opacity-0 pointer-events-none"}`,
32
+ onClick: onClose,
33
+ "aria-hidden": true
34
+ }
35
+ ),
36
+ /* @__PURE__ */ jsxs(
37
+ "div",
38
+ {
39
+ className: `fixed top-0 right-0 h-full w-90 z-50 bg-surface border-l border-outline-variant/40 shadow-2xl flex flex-col transition-transform duration-300 ease-in-out ${open ? "translate-x-0" : "translate-x-full"}`,
40
+ onTouchStart: (e) => {
41
+ touchStartX.current = e.touches[0].clientX;
42
+ },
43
+ onTouchEnd: (e) => {
44
+ if (e.changedTouches[0].clientX - touchStartX.current > 80) onClose();
45
+ },
46
+ "aria-labelledby": "preview-drawer-title",
47
+ role: "dialog",
48
+ "aria-modal": true,
49
+ children: [
50
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-outline-variant/30 shrink-0", children: [
51
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
52
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-lg text-primary", children: "phone_iphone" }),
53
+ /* @__PURE__ */ jsx("h2", { id: "preview-drawer-title", className: "text-sm font-bold text-on-surface", children: drawerTitle })
54
+ ] }),
55
+ /* @__PURE__ */ jsx(IconButton, { icon: "close", variant: "ghost", size: "sm", onClick: onClose, "aria-label": closeAriaLabel })
56
+ ] }),
57
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-4 flex flex-col items-center gap-3", children: [
58
+ /* @__PURE__ */ jsx(
59
+ MobilePreview,
60
+ __spreadProps(__spreadValues({}, previewProps), {
61
+ activeDayIndex: activeDay,
62
+ onActiveDayChange: setActiveDay
63
+ })
64
+ ),
65
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] text-on-surface-variant uppercase tracking-wider font-semibold", children: previewOnlyLabel })
66
+ ] })
67
+ ]
68
+ }
69
+ )
70
+ ] });
71
+ }
72
+ export {
73
+ PreviewDrawer
74
+ };
@@ -0,0 +1,15 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface ProgressBarProps {
4
+ value: number;
5
+ max?: number;
6
+ label?: string;
7
+ showValue?: boolean;
8
+ size?: "sm" | "md" | "lg";
9
+ color?: "primary" | "emerald" | "amber" | "red";
10
+ animated?: boolean;
11
+ className?: string;
12
+ }
13
+ declare function ProgressBar({ value, max, label, showValue, size, color, animated, className, }: ProgressBarProps): react_jsx_runtime.JSX.Element;
14
+
15
+ export { ProgressBar };
@@ -0,0 +1,56 @@
1
+ import "./chunk-ORMEWXMH.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ const SIZE_CLASSES = {
4
+ sm: "h-1.5",
5
+ md: "h-2.5",
6
+ lg: "h-4"
7
+ };
8
+ const COLOR_CLASSES = {
9
+ primary: "bg-primary",
10
+ emerald: "bg-emerald-500",
11
+ amber: "bg-amber-500",
12
+ red: "bg-red-500"
13
+ };
14
+ function ProgressBar({
15
+ value,
16
+ max = 100,
17
+ label,
18
+ showValue = false,
19
+ size = "md",
20
+ color = "primary",
21
+ animated = false,
22
+ className = ""
23
+ }) {
24
+ const percentage = Math.min(100, Math.max(0, value / max * 100));
25
+ const displayPercent = Math.round(percentage);
26
+ return /* @__PURE__ */ jsxs("div", { className: `w-full ${className}`, children: [
27
+ (label || showValue) && /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center mb-1.5", children: [
28
+ label && /* @__PURE__ */ jsx("span", { className: "text-xs text-on-surface-variant", children: label }),
29
+ showValue && /* @__PURE__ */ jsxs("span", { className: "text-xs text-on-surface-variant tabular-nums ml-auto", children: [
30
+ displayPercent,
31
+ "%"
32
+ ] })
33
+ ] }),
34
+ /* @__PURE__ */ jsx(
35
+ "div",
36
+ {
37
+ className: `w-full rounded-full bg-surface-container-high ${SIZE_CLASSES[size]}`,
38
+ role: "progressbar",
39
+ "aria-valuenow": value,
40
+ "aria-valuemin": 0,
41
+ "aria-valuemax": max,
42
+ "aria-label": label,
43
+ children: /* @__PURE__ */ jsx(
44
+ "div",
45
+ {
46
+ className: `${SIZE_CLASSES[size]} rounded-full transition-all duration-500 ${COLOR_CLASSES[color]} ${animated ? "animate-pulse" : ""}`,
47
+ style: { width: `${percentage}%` }
48
+ }
49
+ )
50
+ }
51
+ )
52
+ ] });
53
+ }
54
+ export {
55
+ ProgressBar
56
+ };
@@ -0,0 +1,10 @@
1
+ interface QRCodeDisplayProps {
2
+ url: string;
3
+ size?: number;
4
+ icon?: string;
5
+ iconImageUrl?: string;
6
+ iconSize?: number;
7
+ }
8
+ declare function QRCodeDisplay({ url, size, icon, iconImageUrl, iconSize }: QRCodeDisplayProps): React.ReactNode;
9
+
10
+ export { QRCodeDisplay };
@@ -0,0 +1,43 @@
1
+ "use client";
2
+ import "./chunk-ORMEWXMH.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { useEffect, useState } from "react";
5
+ import QRCode from "qrcode";
6
+ function QRCodeDisplay({ url, size = 200, icon, iconImageUrl, iconSize }) {
7
+ const [dataUrl, setDataUrl] = useState("");
8
+ const hasOverlay = !!(icon || iconImageUrl);
9
+ const resolvedIconSize = iconSize != null ? iconSize : Math.round(size * 0.22);
10
+ useEffect(() => {
11
+ QRCode.toDataURL(url, {
12
+ width: size,
13
+ margin: 2,
14
+ color: { dark: "#1e293b", light: "#ffffff" },
15
+ errorCorrectionLevel: hasOverlay ? "H" : "M"
16
+ }).then(setDataUrl).catch(() => {
17
+ });
18
+ }, [url, size, hasOverlay]);
19
+ if (!dataUrl) {
20
+ return /* @__PURE__ */ jsx("div", { className: "animate-pulse bg-surface-container rounded-xl", style: { width: size, height: size } });
21
+ }
22
+ return /* @__PURE__ */ jsxs("div", { className: "relative inline-flex", style: { width: size, height: size }, children: [
23
+ /* @__PURE__ */ jsx("img", { src: dataUrl, alt: "QR Code", width: size, height: size, className: "rounded-xl" }),
24
+ hasOverlay && /* @__PURE__ */ jsx(
25
+ "div",
26
+ {
27
+ className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white rounded-xl flex items-center justify-center shadow-lg ring-1 ring-black/8 overflow-hidden",
28
+ style: { width: resolvedIconSize + 14, height: resolvedIconSize + 14 },
29
+ children: iconImageUrl ? /* @__PURE__ */ jsx("img", { src: iconImageUrl, alt: "logo", width: resolvedIconSize, height: resolvedIconSize, className: "object-contain" }) : /* @__PURE__ */ jsx(
30
+ "span",
31
+ {
32
+ className: "material-symbols-outlined text-primary",
33
+ style: { fontSize: resolvedIconSize, fontVariationSettings: "'FILL' 1" },
34
+ children: icon
35
+ }
36
+ )
37
+ }
38
+ )
39
+ ] });
40
+ }
41
+ export {
42
+ QRCodeDisplay
43
+ };
@@ -0,0 +1,19 @@
1
+ interface RadioOption {
2
+ value: string;
3
+ label: string;
4
+ description?: string;
5
+ }
6
+ interface RadioGroupProps {
7
+ label?: string;
8
+ options: RadioOption[];
9
+ value: string;
10
+ onChange: (value: string) => void;
11
+ error?: string;
12
+ required?: boolean;
13
+ orientation?: "vertical" | "horizontal";
14
+ disabled?: boolean;
15
+ name?: string;
16
+ }
17
+ declare function RadioGroup({ label, options, value, onChange, error, required, orientation, disabled, name, }: RadioGroupProps): React.ReactNode;
18
+
19
+ export { RadioGroup };
@@ -0,0 +1,78 @@
1
+ "use client";
2
+ import "./chunk-ORMEWXMH.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ function RadioGroup({
5
+ label,
6
+ options,
7
+ value,
8
+ onChange,
9
+ error,
10
+ required,
11
+ orientation = "vertical",
12
+ disabled = false,
13
+ name
14
+ }) {
15
+ var _a;
16
+ const resolvedName = (_a = name != null ? name : label) != null ? _a : "radio-group";
17
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
18
+ label && /* @__PURE__ */ jsxs("span", { className: "text-xs font-bold text-on-surface-variant uppercase tracking-widest px-1", children: [
19
+ label,
20
+ required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
21
+ ] }),
22
+ /* @__PURE__ */ jsx(
23
+ "div",
24
+ {
25
+ role: "group",
26
+ "aria-required": required,
27
+ className: `flex ${orientation === "horizontal" ? "flex-row flex-wrap gap-4" : "flex-col gap-3"}`,
28
+ children: options.map((opt) => {
29
+ const isSelected = opt.value === value;
30
+ const isDisabled = disabled;
31
+ return /* @__PURE__ */ jsxs(
32
+ "label",
33
+ {
34
+ className: `flex items-start gap-3 ${isDisabled ? "cursor-not-allowed opacity-40" : "cursor-pointer"}`,
35
+ children: [
36
+ /* @__PURE__ */ jsxs("span", { className: "relative shrink-0 mt-0.5", children: [
37
+ /* @__PURE__ */ jsx(
38
+ "input",
39
+ {
40
+ type: "radio",
41
+ name: resolvedName,
42
+ value: opt.value,
43
+ checked: isSelected,
44
+ onChange: () => !isDisabled && onChange(opt.value),
45
+ disabled: isDisabled,
46
+ className: "sr-only peer"
47
+ }
48
+ ),
49
+ /* @__PURE__ */ jsx(
50
+ "span",
51
+ {
52
+ className: `flex items-center justify-center w-5 h-5 rounded-full border-2 transition-all
53
+ peer-focus-visible:ring-2 peer-focus-visible:ring-primary/20
54
+ ${isSelected ? "border-primary" : "bg-surface border-outline-variant hover:border-primary/60"}`,
55
+ children: isSelected && /* @__PURE__ */ jsx("span", { className: "w-2.5 h-2.5 rounded-full bg-primary shrink-0" })
56
+ }
57
+ )
58
+ ] }),
59
+ (opt.label || opt.description) && /* @__PURE__ */ jsxs("span", { className: "flex flex-col gap-0.5", children: [
60
+ opt.label && /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-on-surface leading-snug", children: opt.label }),
61
+ opt.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-on-surface-variant leading-snug", children: opt.description })
62
+ ] })
63
+ ]
64
+ },
65
+ opt.value
66
+ );
67
+ })
68
+ }
69
+ ),
70
+ error && /* @__PURE__ */ jsxs("p", { className: "text-xs text-red-500 flex items-center gap-1 px-1", children: [
71
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-sm", children: "error" }),
72
+ error
73
+ ] })
74
+ ] });
75
+ }
76
+ export {
77
+ RadioGroup
78
+ };
@@ -0,0 +1,12 @@
1
+ interface RatingProps {
2
+ value: number;
3
+ onChange?: (value: number) => void;
4
+ max?: number;
5
+ size?: "sm" | "md" | "lg";
6
+ showValue?: boolean;
7
+ label?: string;
8
+ readOnly?: boolean;
9
+ }
10
+ declare function Rating({ value, onChange, max, size, showValue, label, readOnly, }: RatingProps): React.ReactNode;
11
+
12
+ export { Rating, type RatingProps };
package/dist/rating.js ADDED
@@ -0,0 +1,123 @@
1
+ "use client";
2
+ import "./chunk-ORMEWXMH.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { useState } from "react";
5
+ const SIZE_CLASSES = {
6
+ sm: "text-base",
7
+ md: "text-xl",
8
+ lg: "text-3xl"
9
+ };
10
+ function StarIcon({
11
+ fill,
12
+ sizeClass,
13
+ className = ""
14
+ }) {
15
+ if (fill === "half") {
16
+ return /* @__PURE__ */ jsx(
17
+ "span",
18
+ {
19
+ className: `material-symbols-outlined leading-none ${sizeClass} ${className}`,
20
+ style: { fontVariationSettings: "'FILL' 0" },
21
+ "aria-hidden": "true",
22
+ children: "star_half"
23
+ }
24
+ );
25
+ }
26
+ if (fill === "full") {
27
+ return /* @__PURE__ */ jsx(
28
+ "span",
29
+ {
30
+ className: `material-symbols-outlined leading-none ${sizeClass} ${className}`,
31
+ style: { fontVariationSettings: "'FILL' 1" },
32
+ "aria-hidden": "true",
33
+ children: "star"
34
+ }
35
+ );
36
+ }
37
+ return /* @__PURE__ */ jsx(
38
+ "span",
39
+ {
40
+ className: `material-symbols-outlined leading-none ${sizeClass} ${className}`,
41
+ style: { fontVariationSettings: "'FILL' 0" },
42
+ "aria-hidden": "true",
43
+ children: "star"
44
+ }
45
+ );
46
+ }
47
+ function getStarFill(index, value) {
48
+ const starValue = index + 1;
49
+ if (value >= starValue) return "full";
50
+ if (value >= starValue - 0.5) return "half";
51
+ return "empty";
52
+ }
53
+ function Rating({
54
+ value,
55
+ onChange,
56
+ max = 5,
57
+ size = "md",
58
+ showValue = false,
59
+ label,
60
+ readOnly = false
61
+ }) {
62
+ const [hovered, setHovered] = useState(null);
63
+ const isInteractive = !readOnly && !!onChange;
64
+ const sizeClass = SIZE_CLASSES[size];
65
+ const displayValue = hovered !== null ? hovered : value;
66
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
67
+ label && /* @__PURE__ */ jsx("span", { className: "text-xs font-bold text-on-surface-variant uppercase tracking-widest", children: label }),
68
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", children: [
69
+ /* @__PURE__ */ jsx(
70
+ "div",
71
+ {
72
+ className: `flex items-center ${isInteractive ? "cursor-pointer" : ""}`,
73
+ role: isInteractive ? "radiogroup" : void 0,
74
+ "aria-label": label != null ? label : `Rating: ${value} out of ${max}`,
75
+ onMouseLeave: () => isInteractive && setHovered(null),
76
+ onKeyDown: isInteractive ? (e) => {
77
+ if (e.key === "ArrowRight" || e.key === "ArrowUp") {
78
+ e.preventDefault();
79
+ onChange(Math.min(value + 1, max));
80
+ } else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
81
+ e.preventDefault();
82
+ onChange(Math.max(value - 1, 1));
83
+ }
84
+ } : void 0,
85
+ children: Array.from({ length: max }).map((_, i) => {
86
+ const starValue = i + 1;
87
+ const fill = isInteractive && hovered !== null ? hovered >= starValue ? "full" : "empty" : getStarFill(i, displayValue);
88
+ const colorClass = fill === "empty" ? "text-outline-variant" : "text-amber-400";
89
+ if (isInteractive) {
90
+ return /* @__PURE__ */ jsx(
91
+ "button",
92
+ {
93
+ type: "button",
94
+ role: "radio",
95
+ "aria-checked": Math.round(value) === starValue,
96
+ "aria-label": `${starValue} star${starValue !== 1 ? "s" : ""}`,
97
+ className: `${colorClass} transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 rounded active:scale-[0.95]`,
98
+ onMouseEnter: () => setHovered(starValue),
99
+ onClick: () => onChange(starValue),
100
+ children: /* @__PURE__ */ jsx(StarIcon, { fill, sizeClass })
101
+ },
102
+ i
103
+ );
104
+ }
105
+ return /* @__PURE__ */ jsx(
106
+ StarIcon,
107
+ {
108
+ fill,
109
+ sizeClass,
110
+ className: colorClass
111
+ },
112
+ i
113
+ );
114
+ })
115
+ }
116
+ ),
117
+ showValue && /* @__PURE__ */ jsx("span", { className: "ml-1.5 text-sm font-medium text-on-surface-variant", children: value.toFixed(1) })
118
+ ] })
119
+ ] });
120
+ }
121
+ export {
122
+ Rating
123
+ };
@@ -0,0 +1,13 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface RichEditorProps {
4
+ value: string;
5
+ onChange: (html: string) => void;
6
+ placeholder?: string;
7
+ minHeight?: number;
8
+ disabled?: boolean;
9
+ loadingAriaLabel?: string;
10
+ }
11
+ declare function RichEditor({ value, onChange, placeholder, minHeight, disabled, loadingAriaLabel }: RichEditorProps): react_jsx_runtime.JSX.Element;
12
+
13
+ export { RichEditor };