@clubmed/trident-ui 2.0.0-beta.4 → 2.0.0-beta.40
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/chunks/Carousel.context.js +1548 -0
- package/chunks/Carousel.context.js.map +1 -0
- package/chunks/ChatMessage.js +14395 -0
- package/chunks/ChatMessage.js.map +1 -0
- package/chunks/DateField.js +177 -63
- package/chunks/DateField.js.map +1 -1
- package/chunks/Tooltip.js +4461 -0
- package/chunks/Tooltip.js.map +1 -0
- package/chunks/chunk.js +11 -4
- package/chunks/client.js +18953 -0
- package/chunks/client.js.map +1 -0
- package/examples/button-demo.js +2 -2
- package/examples/button-demo.js.map +1 -1
- package/examples/chat-input-demo.d.ts +1 -0
- package/examples/chat-input-demo.js +21 -0
- package/examples/chat-input-demo.js.map +1 -0
- package/examples/chat-message-demo.d.ts +1 -0
- package/examples/chat-message-demo.js +20 -0
- package/examples/chat-message-demo.js.map +1 -0
- package/examples/chat-typing-indicator-demo.d.ts +1 -0
- package/examples/chat-typing-indicator-demo.js +14 -0
- package/examples/chat-typing-indicator-demo.js.map +1 -0
- package/examples/chat-window-demo.d.ts +1 -0
- package/examples/chat-window-demo.js +45 -0
- package/examples/chat-window-demo.js.map +1 -0
- package/examples/checkbox-demo.js +16 -20
- package/examples/checkbox-demo.js.map +1 -1
- package/examples/checkbox-select-demo.d.ts +1 -0
- package/examples/checkbox-select-demo.js +101 -0
- package/examples/checkbox-select-demo.js.map +1 -0
- package/examples/checkboxes-demo.js +6 -6
- package/examples/checkboxes-demo.js.map +1 -1
- package/examples/checkboxes-with-form-control-demo.js +4 -4
- package/examples/choice-expander-demo.js +4 -67
- package/examples/choice-expander-demo.js.map +1 -1
- package/examples/dropdown-demo.js +9 -1
- package/examples/dropdown-demo.js.map +1 -1
- package/examples/elastic-height-demo.js +2 -1
- package/examples/elastic-height-demo.js.map +1 -1
- package/examples/frame-demo.d.ts +1 -0
- package/examples/frame-demo.js +36 -0
- package/examples/frame-demo.js.map +1 -0
- package/examples/frame-horizontal-demo.d.ts +1 -0
- package/examples/frame-horizontal-demo.js +44 -0
- package/examples/frame-horizontal-demo.js.map +1 -0
- package/examples/frame-no-image-demo.d.ts +1 -0
- package/examples/frame-no-image-demo.js +47 -0
- package/examples/frame-no-image-demo.js.map +1 -0
- package/examples/frame-vertical-demo.d.ts +1 -0
- package/examples/frame-vertical-demo.js +44 -0
- package/examples/frame-vertical-demo.js.map +1 -0
- package/examples/frame-with-tag-demo.d.ts +1 -0
- package/examples/frame-with-tag-demo.js +48 -0
- package/examples/frame-with-tag-demo.js.map +1 -0
- package/examples/scrollbar-demo.d.ts +1 -0
- package/examples/scrollbar-demo.js +40 -0
- package/examples/scrollbar-demo.js.map +1 -0
- package/examples/slider-frame-demo.d.ts +1 -0
- package/examples/slider-frame-demo.js +38 -0
- package/examples/slider-frame-demo.js.map +1 -0
- package/examples/slider-frame-with-button-demo.d.ts +1 -0
- package/examples/slider-frame-with-button-demo.js +46 -0
- package/examples/slider-frame-with-button-demo.js.map +1 -0
- package/examples/slider-frame-with-tag-demo.d.ts +1 -0
- package/examples/slider-frame-with-tag-demo.js +50 -0
- package/examples/slider-frame-with-tag-demo.js.map +1 -0
- package/examples/switch-demo.js +1 -1
- package/examples/switch-demo.js.map +1 -1
- package/examples/tooltip-demo.d.ts +1 -0
- package/examples/tooltip-demo.js +103 -0
- package/examples/tooltip-demo.js.map +1 -0
- package/package.json +10 -3
- package/styles/animate.css +19 -19
- package/styles/components.css +82 -0
- package/styles/theme.css +23 -18
- package/styles/ui/button.css +19 -25
- package/styles/ui/chat-button.css +68 -1
- package/styles/ui/controls.css +9 -63
- package/styles/ui/range.css +1 -1
- package/styles/variants.css +0 -2
- package/ui/AdvancedToast.js +11 -11
- package/ui/AdvancedToast.js.map +1 -1
- package/ui/BasicToast.js +1 -1
- package/ui/BasicToast.js.map +1 -1
- package/ui/Breadcrumb.d.ts +2 -2
- package/ui/Breadcrumb.js +31 -35
- package/ui/Breadcrumb.js.map +1 -1
- package/ui/ChatButton.js +1 -1
- package/ui/ChatButton.js.map +1 -1
- package/ui/ChatInput.d.ts +10 -0
- package/ui/ChatInput.js +149 -0
- package/ui/ChatInput.js.map +1 -0
- package/ui/ChatMessage.d.ts +6 -0
- package/ui/ChatMessage.js +2 -0
- package/ui/ChatTypingIndicator.d.ts +2 -0
- package/ui/ChatTypingIndicator.js +30 -0
- package/ui/ChatTypingIndicator.js.map +1 -0
- package/ui/ChatWindow.d.ts +21 -0
- package/ui/ChatWindow.js +108 -0
- package/ui/ChatWindow.js.map +1 -0
- package/ui/ChoiceExpander.js +1 -1
- package/ui/ChoiceExpander.js.map +1 -1
- package/ui/Dropdown.d.ts +20 -3
- package/ui/Dropdown.js +57 -23
- package/ui/Dropdown.js.map +1 -1
- package/ui/ElasticHeight.d.ts +0 -1
- package/ui/ElasticHeight.js +14 -23
- package/ui/ElasticHeight.js.map +1 -1
- package/ui/Loader.d.ts +5 -4
- package/ui/Loader.js +11 -10
- package/ui/Loader.js.map +1 -1
- package/ui/Popin.js +2 -2
- package/ui/Popin.js.map +1 -1
- package/ui/Scrollbar.d.ts +14 -0
- package/ui/Scrollbar.js +68 -0
- package/ui/Scrollbar.js.map +1 -0
- package/ui/SidebarLayout.d.ts +3 -1
- package/ui/SidebarLayout.js +32 -24
- package/ui/SidebarLayout.js.map +1 -1
- package/ui/Tooltip.d.ts +8 -0
- package/ui/Tooltip.js +2 -0
- package/ui/arrows/Arrows.d.ts +1 -0
- package/ui/arrows/Arrows.js +19 -17
- package/ui/arrows/Arrows.js.map +1 -1
- package/ui/buttons/Button.type.d.ts +1 -1
- package/ui/buttons/Button.type.js.map +1 -1
- package/ui/carousel/Carousel.context.d.ts +27 -0
- package/ui/carousel/Carousel.context.js +2 -0
- package/ui/carousel/Carousel.d.ts +3 -0
- package/ui/carousel/Carousel.js +12 -0
- package/ui/carousel/Carousel.js.map +1 -0
- package/ui/carousel/CarouselBody.d.ts +6 -0
- package/ui/carousel/CarouselBody.js +21 -0
- package/ui/carousel/CarouselBody.js.map +1 -0
- package/ui/carousel/CarouselItem.d.ts +6 -0
- package/ui/carousel/CarouselItem.js +21 -0
- package/ui/carousel/CarouselItem.js.map +1 -0
- package/ui/carousel/CarouselNext.d.ts +8 -0
- package/ui/carousel/CarouselNext.js +23 -0
- package/ui/carousel/CarouselNext.js.map +1 -0
- package/ui/carousel/CarouselPrevious.d.ts +8 -0
- package/ui/carousel/CarouselPrevious.js +23 -0
- package/ui/carousel/CarouselPrevious.js.map +1 -0
- package/ui/contexts/TridentUIConfig.js +1 -1
- package/ui/forms/CheckboxSelect.d.ts +31 -0
- package/ui/forms/CheckboxSelect.js +119 -0
- package/ui/forms/CheckboxSelect.js.map +1 -0
- package/ui/forms/Filter.d.ts +8 -4
- package/ui/forms/Filter.js +23 -45
- package/ui/forms/Filter.js.map +1 -1
- package/ui/forms/FormControl.js +6 -6
- package/ui/forms/NumberField.d.ts +10 -10
- package/ui/forms/NumberField.js +8 -8
- package/ui/forms/PhoneField.d.ts +5 -10
- package/ui/forms/PhoneField.js +29 -211
- package/ui/forms/PhoneField.js.map +1 -1
- package/ui/forms/PhoneFieldFullInput.d.ts +16 -0
- package/ui/forms/PhoneFieldFullInput.js +116 -0
- package/ui/forms/PhoneFieldFullInput.js.map +1 -0
- package/ui/forms/PhoneFieldSplitInput.d.ts +19 -0
- package/ui/forms/PhoneFieldSplitInput.js +159 -0
- package/ui/forms/PhoneFieldSplitInput.js.map +1 -0
- package/ui/forms/Select.js +4 -4
- package/ui/forms/Switch.d.ts +3 -3
- package/ui/forms/Switch.js +51 -34
- package/ui/forms/Switch.js.map +1 -1
- package/ui/forms/TextField.js +4 -4
- package/ui/forms/TextField.js.map +1 -1
- package/ui/forms/checkboxes/Checkbox.d.ts +10 -12
- package/ui/forms/checkboxes/Checkbox.js +38 -55
- package/ui/forms/checkboxes/Checkbox.js.map +1 -1
- package/ui/forms/checkboxes/Checkboxes.js +2 -2
- package/ui/forms/checkboxes/Checkboxes.js.map +1 -1
- package/ui/forms/password/Password.js +4 -4
- package/ui/frame/Frame.d.ts +8 -0
- package/ui/frame/Frame.js +16 -0
- package/ui/frame/Frame.js.map +1 -0
- package/ui/frame/FrameLabelsWrapper.d.ts +5 -0
- package/ui/frame/FrameLabelsWrapper.js +12 -0
- package/ui/frame/FrameLabelsWrapper.js.map +1 -0
- package/ui/frame/FrameTitle.d.ts +7 -0
- package/ui/frame/FrameTitle.js +25 -0
- package/ui/frame/FrameTitle.js.map +1 -0
- package/ui/frame/FrameWrapper.d.ts +6 -0
- package/ui/frame/FrameWrapper.js +14 -0
- package/ui/frame/FrameWrapper.js.map +1 -0
- package/ui/frame/SliderFrame.d.ts +14 -0
- package/ui/frame/SliderFrame.js +50 -0
- package/ui/frame/SliderFrame.js.map +1 -0
- package/ui/helpers/colors/colors.d.ts +4 -0
- package/ui/helpers/colors/colors.js +6 -1
- package/ui/helpers/colors/colors.js.map +1 -1
- package/ui/helpers/phone/defaultPrefixes.js +28 -28
- package/ui/helpers/phone/defaultPrefixes.js.map +1 -1
- package/ui/helpers/phone/formatters.d.ts +11 -0
- package/ui/helpers/phone/formatters.js +32 -1
- package/ui/helpers/phone/formatters.js.map +1 -1
- package/ui/helpers/phone/index.d.ts +1 -1
- package/ui/helpers/phone/index.js +3 -3
- package/ui/hooks/useValue.js +1 -1
- package/ui/hooks/useValue.js.map +1 -1
- package/ui/tabs/Tabs.js +1 -1
- package/ui/tabs/Tabs.js.map +1 -1
- package/ui/Breadcrumb.themes.d.ts +0 -8
- package/ui/Breadcrumb.themes.js +0 -16
- package/ui/Breadcrumb.themes.js.map +0 -1
package/styles/ui/controls.css
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
@layer utilities {
|
|
2
2
|
/* This file is for styling the controls of the form inputs (checkbox, radio, switch, filter) */
|
|
3
3
|
/* Styling the controls by their HTML semantic allows us to visually guarantee the non-regression of this semantic */
|
|
4
|
-
/* Radio
|
|
5
|
-
label > span > input[type='checkbox']:not([role='switch']),
|
|
4
|
+
/* Radio */
|
|
6
5
|
label > span > input[type='radio'] {
|
|
7
6
|
opacity: 0;
|
|
8
7
|
position: absolute;
|
|
@@ -10,7 +9,6 @@
|
|
|
10
9
|
height: 100%;
|
|
11
10
|
border: 0;
|
|
12
11
|
}
|
|
13
|
-
label > span > input[type='checkbox']:not([role='switch']) + span,
|
|
14
12
|
label > span > input[type='radio'] + span {
|
|
15
13
|
@apply border transition-all duration-200;
|
|
16
14
|
--tw-ring-color: transparent;
|
|
@@ -20,25 +18,20 @@
|
|
|
20
18
|
align-items: center;
|
|
21
19
|
justify-content: center;
|
|
22
20
|
}
|
|
23
|
-
label > span > input[type='checkbox']:not([role='switch']) + span svg,
|
|
24
21
|
label > span > input[type='radio'] + span svg {
|
|
25
22
|
opacity: 0;
|
|
26
23
|
transform: scale(0);
|
|
27
24
|
transform-origin: center;
|
|
28
25
|
transition: all var(--transition-duration-boop) var(--transition-fn-boop);
|
|
29
26
|
}
|
|
30
|
-
label > span > input[type='checkbox']:not([role='switch']):checked + span svg,
|
|
31
27
|
label > span > input[type='radio']:checked + span svg {
|
|
32
28
|
opacity: 1;
|
|
33
29
|
transform: scale(1);
|
|
34
30
|
}
|
|
35
|
-
label > span > input[type='checkbox']:not([role='switch']):checked + span,
|
|
36
|
-
label > span > input[type='checkbox']:not([role='switch']):not([disabled]):hover + span,
|
|
37
31
|
label > span > input[type='radio']:checked + span,
|
|
38
32
|
label > span > input[type='radio']:not([disabled]):hover + span {
|
|
39
33
|
@apply border-black;
|
|
40
34
|
}
|
|
41
|
-
label > span > input[type='checkbox']:not([role='switch']):focus-visible + span,
|
|
42
35
|
label > span > input[type='radio']:focus-visible + span {
|
|
43
36
|
@apply ring-8;
|
|
44
37
|
--tw-ring-color: color-mix(in oklab, var(--color-lavender) 20%, transparent);
|
|
@@ -46,9 +39,6 @@
|
|
|
46
39
|
label > span > input[type='radio'] + span {
|
|
47
40
|
@apply rounded-full;
|
|
48
41
|
}
|
|
49
|
-
label > span > input[type='checkbox']:not([role='switch']) + span {
|
|
50
|
-
border-radius: calc(var(--spacing) * 8);
|
|
51
|
-
}
|
|
52
42
|
/* Filter */
|
|
53
43
|
@media (hover: hover) and (pointer: fine) {
|
|
54
44
|
label:active > input[type='checkbox'][data-name='Filter']:not(:disabled) {
|
|
@@ -100,62 +90,21 @@
|
|
|
100
90
|
--tw-ring-color: color-mix(in oklab, var(--color-lavender) 20%, transparent);
|
|
101
91
|
}
|
|
102
92
|
/* Switch */
|
|
103
|
-
|
|
104
|
-
@apply transition-all;
|
|
93
|
+
input[role='switch'][data-name='Switch'] + span {
|
|
105
94
|
@apply relative flex items-center transition-colors duration-200;
|
|
106
95
|
@apply ring-0;
|
|
107
96
|
--tw-ring-color: color-mix(in oklab, var(--color-lavender) 0%, transparent);
|
|
108
|
-
background-color: var(--color-middleGrey);
|
|
109
|
-
border-radius: var(--radius-pill);
|
|
110
|
-
}
|
|
111
|
-
label > span > input[type='checkbox'][role='switch'] + span > svg {
|
|
112
|
-
transition: all var(--transition-duration-boop) var(--transition-fn-boop);
|
|
113
|
-
transform: translate(calc(0% + 4px));
|
|
114
|
-
}
|
|
115
|
-
label > span > input[type='checkbox'][role='switch'] + span > svg circle + circle {
|
|
116
|
-
opacity: 1;
|
|
117
|
-
transition: opacity var(--transition-duration-boop) var(--transition-fn-boop);
|
|
118
|
-
}
|
|
119
|
-
label > span > input[type='checkbox'][role='switch'] + span > svg path {
|
|
120
|
-
opacity: 0;
|
|
121
|
-
scale: 0;
|
|
122
|
-
transform-origin: center;
|
|
123
|
-
transition:
|
|
124
|
-
opacity var(--transition-duration-boop) var(--transition-fn-boop),
|
|
125
|
-
scale var(--transition-duration-boop) var(--transition-fn-boop);
|
|
126
|
-
}
|
|
127
|
-
label > span > input[type='checkbox'][role='switch']:checked + span {
|
|
128
|
-
background-color: var(--color-saffron);
|
|
129
|
-
}
|
|
130
|
-
label > span > input[type='checkbox'][role='switch']:checked + span > svg {
|
|
131
|
-
transform: translate(calc(100% + 4px));
|
|
132
|
-
}
|
|
133
|
-
label > span > input[type='checkbox'][role='switch']:checked + span > svg circle + circle {
|
|
134
|
-
opacity: 0;
|
|
135
|
-
}
|
|
136
|
-
label > span > input[type='checkbox'][role='switch']:checked + span > svg path {
|
|
137
|
-
scale: 1;
|
|
138
|
-
opacity: 1;
|
|
139
|
-
}
|
|
140
|
-
label > span > input[type='checkbox'][role='switch']:focus-visible + span {
|
|
141
|
-
@apply ring-8;
|
|
142
|
-
--tw-ring-color: color-mix(in oklab, var(--color-lavender) 20%, transparent);
|
|
143
97
|
}
|
|
144
98
|
|
|
145
|
-
|
|
146
|
-
@apply relative flex items-center transition-colors duration-200;
|
|
147
|
-
@apply ring-0;
|
|
148
|
-
--tw-ring-color: color-mix(in oklab, var(--color-lavender) 0%, transparent);
|
|
149
|
-
}
|
|
150
|
-
button[role='switch'][data-name='Switch'] svg {
|
|
99
|
+
input[role='switch'][data-name='Switch'] + span svg {
|
|
151
100
|
transition: all var(--transition-duration-boop) var(--transition-fn-boop);
|
|
152
101
|
transform: translate(calc(0%));
|
|
153
102
|
}
|
|
154
|
-
|
|
103
|
+
input[role='switch'][data-name='Switch'] + span svg circle + circle {
|
|
155
104
|
opacity: 1;
|
|
156
105
|
transition: opacity var(--transition-duration-boop) var(--transition-fn-boop);
|
|
157
106
|
}
|
|
158
|
-
|
|
107
|
+
input[role='switch'][data-name='Switch'] + span svg path {
|
|
159
108
|
opacity: 0;
|
|
160
109
|
scale: 0;
|
|
161
110
|
transform-origin: center;
|
|
@@ -163,20 +112,17 @@
|
|
|
163
112
|
opacity var(--transition-duration-boop) var(--transition-fn-boop),
|
|
164
113
|
scale var(--transition-duration-boop) var(--transition-fn-boop);
|
|
165
114
|
}
|
|
166
|
-
|
|
167
|
-
background-color: var(--color-saffron);
|
|
168
|
-
}
|
|
169
|
-
button[role='switch'][data-name='Switch'][aria-checked='true'] svg {
|
|
115
|
+
input[role='switch'][data-name='Switch']:checked + span svg {
|
|
170
116
|
transform: translate(calc(100%));
|
|
171
117
|
}
|
|
172
|
-
|
|
118
|
+
input[role='switch'][data-name='Switch']:checked + span svg circle + circle {
|
|
173
119
|
opacity: 0;
|
|
174
120
|
}
|
|
175
|
-
|
|
121
|
+
input[role='switch'][data-name='Switch']:checked + span svg path {
|
|
176
122
|
scale: 1;
|
|
177
123
|
opacity: 1;
|
|
178
124
|
}
|
|
179
|
-
|
|
125
|
+
input[role='switch'][data-name='Switch']:focus-visible + span {
|
|
180
126
|
@apply ring-8;
|
|
181
127
|
--tw-ring-color: color-mix(in oklab, var(--color-lavender) 20%, transparent);
|
|
182
128
|
}
|
package/styles/ui/range.css
CHANGED
package/styles/variants.css
CHANGED
package/ui/AdvancedToast.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { t as e } from "../chunks/clsx.js";
|
|
3
|
-
import t from "./
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
3
|
+
import { ElasticHeight as t } from "./ElasticHeight.js";
|
|
4
|
+
import { Colors as n, getBgColor as r, getBorderColor as i, getTextColor as a } from "./helpers/colors/colors.js";
|
|
5
|
+
import { useCountdown as o } from "./hooks/useCountdown.js";
|
|
6
|
+
import s from "./hooks/useResizeObserver.js";
|
|
7
7
|
import { useEffect as c, useId as l, useRef as u, useState as d } from "react";
|
|
8
8
|
import { Icon as f } from "@clubmed/trident-icons";
|
|
9
9
|
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
10
10
|
//#region lib/ui/AdvancedToast.tsx
|
|
11
|
-
var h = ({ className: h, title: g, body: _, icon: v, isVisible: y, labels: b, children: x, accentColor: S =
|
|
12
|
-
let O = l(), [k, A] = d(!1), [j, M] = d(!1), N = u(null), { progress: P } =
|
|
11
|
+
var h = ({ className: h, title: g, body: _, icon: v, isVisible: y, labels: b, children: x, accentColor: S = n.orange, duration: C = 1e4, onClose: w, toastId: T, updateHeight: E, ...D }) => {
|
|
12
|
+
let O = l(), [k, A] = d(!1), [j, M] = d(!1), N = u(null), { progress: P } = o({
|
|
13
13
|
duration: C,
|
|
14
14
|
isPaused: j,
|
|
15
15
|
isVisible: y
|
|
@@ -18,7 +18,7 @@ var h = ({ className: h, title: g, body: _, icon: v, isVisible: y, labels: b, ch
|
|
|
18
18
|
P === 100 && w?.();
|
|
19
19
|
}, [P, w]);
|
|
20
20
|
let F = () => M(!0), I = () => M(!1), L = () => M(!1);
|
|
21
|
-
return
|
|
21
|
+
return s({
|
|
22
22
|
ref: N,
|
|
23
23
|
onResize: () => {
|
|
24
24
|
N.current && E(T, N.current.offsetHeight);
|
|
@@ -26,7 +26,7 @@ var h = ({ className: h, title: g, body: _, icon: v, isVisible: y, labels: b, ch
|
|
|
26
26
|
}), /* @__PURE__ */ m("div", {
|
|
27
27
|
ref: N,
|
|
28
28
|
...D,
|
|
29
|
-
className: e(y ? "animate-
|
|
29
|
+
className: e(y ? "animate-slide-right-enter" : "animate-slide-right-exit opacity-0", "relative isolate flex gap-x-20 rounded-16 bg-white p-20 text-black transition-opacity duration-300 border-1 overflow-hidden", i(S), h),
|
|
30
30
|
role: "note",
|
|
31
31
|
onMouseDown: F,
|
|
32
32
|
onMouseUp: I,
|
|
@@ -35,7 +35,7 @@ var h = ({ className: h, title: g, body: _, icon: v, isVisible: y, labels: b, ch
|
|
|
35
35
|
/* @__PURE__ */ p(f, {
|
|
36
36
|
name: v,
|
|
37
37
|
width: "30px",
|
|
38
|
-
className: e("flex-shrink-0 pt-4",
|
|
38
|
+
className: e("flex-shrink-0 pt-4", a(S))
|
|
39
39
|
}),
|
|
40
40
|
/* @__PURE__ */ m("div", {
|
|
41
41
|
className: "flex flex-1 flex-col gap-12 pe-24 text-b4",
|
|
@@ -54,7 +54,7 @@ var h = ({ className: h, title: g, body: _, icon: v, isVisible: y, labels: b, ch
|
|
|
54
54
|
style: { rotate: k ? "180deg" : "0deg" },
|
|
55
55
|
className: "transition-all duration-300"
|
|
56
56
|
})]
|
|
57
|
-
}), /* @__PURE__ */ p(
|
|
57
|
+
}), /* @__PURE__ */ p(t, {
|
|
58
58
|
isExpanded: k,
|
|
59
59
|
children: /* @__PURE__ */ p("div", {
|
|
60
60
|
className: "pt-4",
|
|
@@ -76,7 +76,7 @@ var h = ({ className: h, title: g, body: _, icon: v, isVisible: y, labels: b, ch
|
|
|
76
76
|
C !== !1 && /* @__PURE__ */ p("div", {
|
|
77
77
|
className: "absolute bottom-0 left-0 right-0 h-4 overflow-hidden rounded-b-16",
|
|
78
78
|
children: /* @__PURE__ */ p("div", {
|
|
79
|
-
className:
|
|
79
|
+
className: r(S),
|
|
80
80
|
style: {
|
|
81
81
|
width: "100%",
|
|
82
82
|
height: "100%",
|
package/ui/AdvancedToast.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdvancedToast.js","names":[],"sources":["../../lib/ui/AdvancedToast.tsx"],"sourcesContent":["'use client';\n\nimport clsx from 'clsx';\nimport {\n useEffect,\n useId,\n useRef,\n useState,\n type ComponentPropsWithoutRef,\n type FunctionComponent,\n} from 'react';\n\nimport { Icon, type IconicNames } from '@clubmed/trident-icons';\nimport { ElasticHeight } from './ElasticHeight';\nimport { Colors, getBgColor, getBorderColor, getTextColor } from './helpers/colors/colors';\nimport { useCountdown } from './hooks/useCountdown';\nimport useResizeObserver from './hooks/useResizeObserver';\n\ninterface Props extends ComponentPropsWithoutRef<'div'> {\n title: string;\n body: string;\n icon: IconicNames;\n isVisible: boolean;\n labels: {\n close: string;\n details: string;\n };\n onClose?: () => void;\n children?: React.ReactNode;\n accentColor?: Colors;\n moreInfoLabel?: string;\n moreInfoHref?: string;\n duration?: false | number;\n toastId: string;\n updateHeight: (id: string, height: number) => void;\n}\n\nexport const AdvancedToast: FunctionComponent<Props> = ({\n className,\n title,\n body,\n icon,\n isVisible,\n labels,\n children,\n accentColor = Colors.orange,\n duration = 10000,\n onClose,\n toastId,\n updateHeight,\n ...attrs\n}) => {\n const id = useId();\n const [isDetailsExpanded, setIsDetailsExpanded] = useState(false);\n const [isPaused, setIsPaused] = useState(false);\n const toastRef = useRef<HTMLDivElement>(null!);\n\n const { progress } = useCountdown({ duration, isPaused, isVisible });\n useEffect(() => {\n if (progress === 100) {\n onClose?.();\n }\n }, [progress, onClose]);\n\n const handleMouseDown = () => setIsPaused(true);\n const handleMouseUp = () => setIsPaused(false);\n const handleMouseLeave = () => setIsPaused(false);\n\n useResizeObserver<HTMLDivElement>({\n ref: toastRef,\n onResize: () => {\n if (toastRef.current) {\n updateHeight(toastId, toastRef.current.offsetHeight);\n }\n },\n });\n\n return (\n <div\n ref={toastRef}\n {...attrs}\n className={clsx(\n isVisible ? 'animate-
|
|
1
|
+
{"version":3,"file":"AdvancedToast.js","names":[],"sources":["../../lib/ui/AdvancedToast.tsx"],"sourcesContent":["'use client';\n\nimport clsx from 'clsx';\nimport {\n useEffect,\n useId,\n useRef,\n useState,\n type ComponentPropsWithoutRef,\n type FunctionComponent,\n} from 'react';\n\nimport { Icon, type IconicNames } from '@clubmed/trident-icons';\nimport { ElasticHeight } from './ElasticHeight';\nimport { Colors, getBgColor, getBorderColor, getTextColor } from './helpers/colors/colors';\nimport { useCountdown } from './hooks/useCountdown';\nimport useResizeObserver from './hooks/useResizeObserver';\n\ninterface Props extends ComponentPropsWithoutRef<'div'> {\n title: string;\n body: string;\n icon: IconicNames;\n isVisible: boolean;\n labels: {\n close: string;\n details: string;\n };\n onClose?: () => void;\n children?: React.ReactNode;\n accentColor?: Colors;\n moreInfoLabel?: string;\n moreInfoHref?: string;\n duration?: false | number;\n toastId: string;\n updateHeight: (id: string, height: number) => void;\n}\n\nexport const AdvancedToast: FunctionComponent<Props> = ({\n className,\n title,\n body,\n icon,\n isVisible,\n labels,\n children,\n accentColor = Colors.orange,\n duration = 10000,\n onClose,\n toastId,\n updateHeight,\n ...attrs\n}) => {\n const id = useId();\n const [isDetailsExpanded, setIsDetailsExpanded] = useState(false);\n const [isPaused, setIsPaused] = useState(false);\n const toastRef = useRef<HTMLDivElement>(null!);\n\n const { progress } = useCountdown({ duration, isPaused, isVisible });\n useEffect(() => {\n if (progress === 100) {\n onClose?.();\n }\n }, [progress, onClose]);\n\n const handleMouseDown = () => setIsPaused(true);\n const handleMouseUp = () => setIsPaused(false);\n const handleMouseLeave = () => setIsPaused(false);\n\n useResizeObserver<HTMLDivElement>({\n ref: toastRef,\n onResize: () => {\n if (toastRef.current) {\n updateHeight(toastId, toastRef.current.offsetHeight);\n }\n },\n });\n\n return (\n <div\n ref={toastRef}\n {...attrs}\n className={clsx(\n isVisible ? 'animate-slide-right-enter' : 'animate-slide-right-exit opacity-0',\n 'relative isolate flex gap-x-20 rounded-16 bg-white p-20 text-black transition-opacity duration-300 border-1 overflow-hidden',\n getBorderColor(accentColor),\n className,\n )}\n role=\"note\"\n onMouseDown={handleMouseDown}\n onMouseUp={handleMouseUp}\n onMouseLeave={handleMouseLeave}\n >\n <Icon\n name={icon}\n width=\"30px\"\n className={clsx('flex-shrink-0 pt-4', getTextColor(accentColor))}\n />\n <div className=\"flex flex-1 flex-col gap-12 pe-24 text-b4\">\n <div>\n <p className=\" font-bold\" id={id}>\n {title}\n </p>\n <p>{body}</p>\n </div>\n\n {children && (\n <div>\n <button\n onClick={() => setIsDetailsExpanded(!isDetailsExpanded)}\n aria-expanded={isDetailsExpanded}\n className=\"flex w-fit items-center gap-8 text-b5 font-normal\"\n type=\"button\"\n >\n <span>{labels.details}</span>\n <Icon\n name=\"ArrowDefaultDown\"\n width=\"24px\"\n style={{ rotate: isDetailsExpanded ? '180deg' : '0deg' }}\n className=\"transition-all duration-300\"\n />\n </button>\n <ElasticHeight isExpanded={isDetailsExpanded}>\n <div className=\"pt-4\">{children}</div>\n </ElasticHeight>\n </div>\n )}\n </div>\n\n {onClose && (\n <button\n aria-describedby={id}\n aria-label={labels.close}\n className=\"absolute end-0 top-0 mt-20 me-13 text-black\"\n onClick={onClose}\n type=\"button\"\n >\n <Icon name=\"CrossDefault\" width=\"24px\" />\n </button>\n )}\n\n {duration !== false && (\n <div className=\"absolute bottom-0 left-0 right-0 h-4 overflow-hidden rounded-b-16\">\n <div\n className={getBgColor(accentColor)}\n style={{\n width: '100%',\n height: '100%',\n transform: `scaleX(${(100 - progress) / 100})`,\n transformOrigin: 'left',\n transition: 'transform 16ms linear',\n }}\n />\n </div>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;AAqCA,IAAa,KAA2C,EACtD,cACA,UACA,SACA,SACA,cACA,WACA,aACA,iBAAc,EAAO,QACrB,cAAW,KACX,YACA,YACA,iBACA,GAAG,QACC;CACJ,IAAM,IAAK,GAAO,EACZ,CAAC,GAAmB,KAAwB,EAAS,GAAM,EAC3D,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,IAAW,EAAuB,KAAM,EAExC,EAAE,gBAAa,EAAa;EAAE;EAAU;EAAU;EAAW,CAAC;AACpE,SAAgB;AACd,EAAI,MAAa,OACf,KAAW;IAEZ,CAAC,GAAU,EAAQ,CAAC;CAEvB,IAAM,UAAwB,EAAY,GAAK,EACzC,UAAsB,EAAY,GAAM,EACxC,UAAyB,EAAY,GAAM;AAWjD,QATA,EAAkC;EAChC,KAAK;EACL,gBAAgB;AACd,GAAI,EAAS,WACX,EAAa,GAAS,EAAS,QAAQ,aAAa;;EAGzD,CAAC,EAGA,kBAAC,OAAD;EACE,KAAK;EACL,GAAI;EACJ,WAAW,EACT,IAAY,8BAA8B,sCAC1C,+HACA,EAAe,EAAY,EAC3B,EACD;EACD,MAAK;EACL,aAAa;EACb,WAAW;EACX,cAAc;YAZhB;GAcE,kBAAC,GAAD;IACE,MAAM;IACN,OAAM;IACN,WAAW,EAAK,sBAAsB,EAAa,EAAY,CAAA;IAC/D,CAAA;GACF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;KAAiB;eAC3B;KACC,CAAA,EACJ,kBAAC,KAAD,EAAA,UAAI,GAAS,CAAA,CACT,EAAA,CAAA,EAEL,KACC,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,UAAD;KACE,eAAe,EAAqB,CAAC,EAAkB;KACvD,iBAAe;KACf,WAAU;KACV,MAAK;eAJP,CAME,kBAAC,QAAD,EAAA,UAAO,EAAO,SAAe,CAAA,EAC7B,kBAAC,GAAD;MACE,MAAK;MACL,OAAM;MACN,OAAO,EAAE,QAAQ,IAAoB,WAAW,QAAQ;MACxD,WAAU;MACV,CAAA,CAAA;QAEJ,kBAAC,GAAD;KAAe,YAAY;eACzB,kBAAC,OAAD;MAAK,WAAU;MAAQ;MAAe,CAAA;KACxB,CAAA,CACZ,EAAA,CAAA,CAAA;;GAIT,KACC,kBAAC,UAAD;IACE,oBAAkB;IAClB,cAAY,EAAO;IACnB,WAAU;IACV,SAAS;IACT,MAAK;cAEL,kBAAC,GAAD;KAAM,MAAK;KAAe,OAAM;KAAS,CAAA;IAClC,CAAA;GAGV,MAAa,MACZ,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KACE,WAAW,EAAW,EAAY;KAClC,OAAO;MACL,OAAO;MACP,QAAQ;MACR,WAAW,WAAW,MAAM,KAAY,IAAI;MAC5C,iBAAiB;MACjB,YAAY;;KAEd,CAAA;IACE,CAAA"}
|
package/ui/BasicToast.js
CHANGED
|
@@ -10,7 +10,7 @@ function l({ className: l, description: u, href: d, icon: f, incentive: p, isVis
|
|
|
10
10
|
let S = r(v), C = a(), w = b ?? u;
|
|
11
11
|
return /* @__PURE__ */ c("div", {
|
|
12
12
|
...x,
|
|
13
|
-
className: e(m ? "animate-
|
|
13
|
+
className: e(m ? "animate-slide-right-enter" : "animate-slide-right-exit opacity-0", "relative isolate flex gap-x-20 rounded-16 p-20 transition-opacity duration-300", t(S), n(S), l),
|
|
14
14
|
role: "note",
|
|
15
15
|
children: [
|
|
16
16
|
f && /* @__PURE__ */ s(o, {
|
package/ui/BasicToast.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BasicToast.js","names":[],"sources":["../../lib/ui/BasicToast.tsx"],"sourcesContent":["'use client';\nimport clsx from 'clsx';\nimport { type ComponentPropsWithoutRef, type PropsWithChildren, useId } from 'react';\n\nimport { Icon, type IconicNames } from '@clubmed/trident-icons';\nimport { getBgColor, getComplementaryTextColor, getThemeColor } from './helpers/colors/colors';\nimport { Link } from './Link';\nimport type { Theme } from './types/Theme';\n\nexport interface BasicToastProps extends ComponentPropsWithoutRef<'div'> {\n /**\n * @deprecated use children slot instead\n */\n description?: string;\n href: string;\n icon?: IconicNames;\n incentive: string;\n isVisible: boolean;\n labels: { close: string };\n onClick?: () => void;\n onClose?: () => void;\n theme: Theme;\n title: string;\n}\n\nexport function BasicToast({\n className,\n description,\n href,\n icon,\n incentive,\n isVisible,\n labels,\n onClick,\n onClose,\n theme = 'dark',\n title,\n children,\n ...attrs\n}: PropsWithChildren<BasicToastProps>) {\n const color = getThemeColor(theme);\n const id = useId();\n const content = children ?? description;\n\n return (\n <div\n {...attrs}\n className={clsx(\n isVisible ? 'animate-
|
|
1
|
+
{"version":3,"file":"BasicToast.js","names":[],"sources":["../../lib/ui/BasicToast.tsx"],"sourcesContent":["'use client';\nimport clsx from 'clsx';\nimport { type ComponentPropsWithoutRef, type PropsWithChildren, useId } from 'react';\n\nimport { Icon, type IconicNames } from '@clubmed/trident-icons';\nimport { getBgColor, getComplementaryTextColor, getThemeColor } from './helpers/colors/colors';\nimport { Link } from './Link';\nimport type { Theme } from './types/Theme';\n\nexport interface BasicToastProps extends ComponentPropsWithoutRef<'div'> {\n /**\n * @deprecated use children slot instead\n */\n description?: string;\n href: string;\n icon?: IconicNames;\n incentive: string;\n isVisible: boolean;\n labels: { close: string };\n onClick?: () => void;\n onClose?: () => void;\n theme: Theme;\n title: string;\n}\n\nexport function BasicToast({\n className,\n description,\n href,\n icon,\n incentive,\n isVisible,\n labels,\n onClick,\n onClose,\n theme = 'dark',\n title,\n children,\n ...attrs\n}: PropsWithChildren<BasicToastProps>) {\n const color = getThemeColor(theme);\n const id = useId();\n const content = children ?? description;\n\n return (\n <div\n {...attrs}\n className={clsx(\n isVisible ? 'animate-slide-right-enter' : 'animate-slide-right-exit opacity-0',\n 'relative isolate flex gap-x-20 rounded-16 p-20 transition-opacity duration-300',\n getBgColor(color),\n getComplementaryTextColor(color),\n className,\n )}\n role=\"note\"\n >\n {icon && <Icon name={icon} width=\"30px\" />}\n <a className=\"group pe-44\" href={href} onClick={onClick}>\n <p className=\"mb-4 text-b3 font-bold\" id={id}>\n {title}\n </p>\n {content && <div className=\"mb-12 text-b4\">{content}</div>}\n <Link component=\"span\" className=\"text-b3 font-bold\" label={incentive} />\n </a>\n {onClose && (\n <button\n aria-describedby={id}\n aria-label={labels.close}\n className={clsx('absolute end-0 top-0 m-20', getComplementaryTextColor(color))}\n onClick={onClose}\n >\n <Icon name=\"CrossDefault\" width=\"24px\" />\n </button>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAyBA,SAAgB,EAAW,EACzB,cACA,gBACA,SACA,SACA,cACA,cACA,WACA,YACA,YACA,WAAQ,QACR,UACA,aACA,GAAG,KACkC;CACrC,IAAM,IAAQ,EAAc,EAAM,EAC5B,IAAK,GAAO,EACZ,IAAU,KAAY;AAE5B,QACE,kBAAC,OAAD;EACE,GAAI;EACJ,WAAW,EACT,IAAY,8BAA8B,sCAC1C,kFACA,EAAW,EAAM,EACjB,EAA0B,EAAM,EAChC,EACD;EACD,MAAK;YATP;GAWG,KAAQ,kBAAC,GAAD;IAAM,MAAM;IAAM,OAAM;IAAS,CAAA;GAC1C,kBAAC,KAAD;IAAG,WAAU;IAAoB;IAAe;cAAhD;KACE,kBAAC,KAAD;MAAG,WAAU;MAA6B;gBACvC;MACC,CAAA;KACH,KAAW,kBAAC,OAAD;MAAK,WAAU;gBAAiB;MAAc,CAAA;KAC1D,kBAAC,GAAD;MAAM,WAAU;MAAO,WAAU;MAAoB,OAAO;MAAa,CAAA;;;GAE1E,KACC,kBAAC,UAAD;IACE,oBAAkB;IAClB,cAAY,EAAO;IACnB,WAAW,EAAK,6BAA6B,EAA0B,EAAM,CAAC;IAC9E,SAAS;cAET,kBAAC,GAAD;KAAM,MAAK;KAAe,OAAM;KAAS,CAAA;IAClC,CAAA"}
|
package/ui/Breadcrumb.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ComponentPropsWithoutRef, FunctionComponent } from 'react';
|
|
2
2
|
import { Theme } from './types/Theme';
|
|
3
3
|
export interface BreadcrumbProps extends ComponentPropsWithoutRef<'nav'> {
|
|
4
|
-
items:
|
|
4
|
+
items: Array<{
|
|
5
5
|
href: string;
|
|
6
6
|
label: string;
|
|
7
|
-
}
|
|
7
|
+
}>;
|
|
8
8
|
theme?: Theme;
|
|
9
9
|
}
|
|
10
10
|
export declare const Breadcrumb: FunctionComponent<BreadcrumbProps>;
|
package/ui/Breadcrumb.js
CHANGED
|
@@ -1,43 +1,39 @@
|
|
|
1
1
|
import { t as e } from "../chunks/clsx.js";
|
|
2
|
-
import {
|
|
3
|
-
import { createElement as
|
|
4
|
-
import { Icon as
|
|
5
|
-
import { jsx as
|
|
2
|
+
import { getComplementaryTextColor as t, getThemeColor as n } from "./helpers/colors/colors.js";
|
|
3
|
+
import { createElement as r } from "react";
|
|
4
|
+
import { Icon as i } from "@clubmed/trident-icons";
|
|
5
|
+
import { jsx as a } from "react/jsx-runtime";
|
|
6
6
|
//#region lib/ui/Breadcrumb.tsx
|
|
7
|
-
var
|
|
8
|
-
if (
|
|
9
|
-
let
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
var o = ({ className: o, items: s, theme: c = "light", ...l }) => {
|
|
8
|
+
if (s.length == 0) return null;
|
|
9
|
+
let u = t(n(c)), d = u === "text-white" ? u : "text-middleGrey", [f, p, , ...m] = s, h = m.length ? m[m.length - 1] : s[s.length - 1], g = m.length ? [
|
|
10
|
+
f,
|
|
11
|
+
p,
|
|
12
12
|
{
|
|
13
|
-
label: "",
|
|
14
|
-
href: ""
|
|
15
|
-
className: "breadcrumb-spacer"
|
|
13
|
+
label: "…",
|
|
14
|
+
href: ""
|
|
16
15
|
},
|
|
17
|
-
|
|
18
|
-
] :
|
|
19
|
-
return /* @__PURE__ */
|
|
20
|
-
role: "navigation",
|
|
16
|
+
h
|
|
17
|
+
] : s;
|
|
18
|
+
return /* @__PURE__ */ a("nav", {
|
|
21
19
|
"aria-label": "Breadcrumb",
|
|
22
|
-
...
|
|
23
|
-
className: e(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}, u ? /* @__PURE__ */ i("a", {
|
|
38
|
-
href: a,
|
|
20
|
+
...l,
|
|
21
|
+
className: e(o, "overflow-hidden"),
|
|
22
|
+
children: /* @__PURE__ */ a("ul", {
|
|
23
|
+
className: "scrollbar-hidden text-b4 flex items-center overflow-x-auto font-sans",
|
|
24
|
+
children: g.map(({ label: t, href: n }, o) => {
|
|
25
|
+
let s = o === g.length - 1, c = n && !s;
|
|
26
|
+
return /* @__PURE__ */ r("li", {
|
|
27
|
+
...s && { "aria-current": "page" },
|
|
28
|
+
className: e("flex shrink-0 items-center", { [d]: !s }, {
|
|
29
|
+
"font-bold": s,
|
|
30
|
+
[u]: s
|
|
31
|
+
}),
|
|
32
|
+
key: t
|
|
33
|
+
}, c ? /* @__PURE__ */ a("a", {
|
|
34
|
+
href: n,
|
|
39
35
|
children: t
|
|
40
|
-
}) : t, !
|
|
36
|
+
}) : t, !s && /* @__PURE__ */ a(i, {
|
|
41
37
|
className: "mx-4",
|
|
42
38
|
name: "Diamond",
|
|
43
39
|
width: "14px"
|
|
@@ -47,6 +43,6 @@ var a = ({ className: a, items: o, theme: s = "light", ...c }) => {
|
|
|
47
43
|
});
|
|
48
44
|
};
|
|
49
45
|
//#endregion
|
|
50
|
-
export {
|
|
46
|
+
export { o as Breadcrumb };
|
|
51
47
|
|
|
52
48
|
//# sourceMappingURL=Breadcrumb.js.map
|
package/ui/Breadcrumb.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Breadcrumb.js","names":[],"sources":["../../lib/ui/Breadcrumb.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport type { ComponentPropsWithoutRef, FunctionComponent } from 'react';\n\nimport {
|
|
1
|
+
{"version":3,"file":"Breadcrumb.js","names":[],"sources":["../../lib/ui/Breadcrumb.tsx"],"sourcesContent":["import clsx from 'clsx';\nimport type { ComponentPropsWithoutRef, FunctionComponent } from 'react';\n\nimport type { Theme } from './types/Theme';\nimport { Icon } from '@clubmed/trident-icons';\nimport { getComplementaryTextColor, getThemeColor } from './helpers/colors/colors';\n\nexport interface BreadcrumbProps extends ComponentPropsWithoutRef<'nav'> {\n items: Array<{\n href: string;\n label: string;\n }>;\n theme?: Theme;\n}\n\nexport const Breadcrumb: FunctionComponent<BreadcrumbProps> = ({\n className,\n items,\n theme = 'light',\n ...attrs\n}) => {\n if (items.length == 0) {\n return null;\n }\n\n const themeColor = getThemeColor(theme);\n const accentColor = getComplementaryTextColor(themeColor);\n const color = accentColor === 'text-white' ? accentColor : 'text-middleGrey';\n\n const [first, second, , ...rest] = items;\n const lastItem = rest.length ? rest[rest.length - 1] : items[items.length - 1];\n const spacer = { label: '…', href: '' };\n const itemsToRender = !rest.length ? items : [first, second, spacer, lastItem];\n\n return (\n <nav aria-label=\"Breadcrumb\" {...attrs} className={clsx(className, 'overflow-hidden')}>\n <ul className=\"scrollbar-hidden text-b4 flex items-center overflow-x-auto font-sans\">\n {itemsToRender.map(({ label, href }, index) => {\n const isLast = index === itemsToRender.length - 1;\n const isLink = href && !isLast;\n\n return (\n <li\n {...(isLast && { 'aria-current': 'page' })}\n className={clsx(\n 'flex shrink-0 items-center',\n { [color]: !isLast },\n {\n 'font-bold': isLast,\n [accentColor]: isLast,\n },\n )}\n key={label}\n >\n {isLink ? <a href={href}>{label}</a> : label}\n {!isLast && <Icon className=\"mx-4\" name=\"Diamond\" width=\"14px\" />}\n </li>\n );\n })}\n </ul>\n </nav>\n );\n};\n"],"mappings":";;;;;;AAeA,IAAa,KAAkD,EAC7D,cACA,UACA,WAAQ,SACR,GAAG,QACC;AACJ,KAAI,EAAM,UAAU,EAClB,QAAO;CAIT,IAAM,IAAc,EADD,EAAc,EAAM,CACkB,EACnD,IAAQ,MAAgB,eAAe,IAAc,mBAErD,CAAC,GAAO,KAAU,GAAG,KAAQ,GAC7B,IAAW,EAAK,SAAS,EAAK,EAAK,SAAS,KAAK,EAAM,EAAM,SAAS,IAEtE,IAAiB,EAAK,SAAiB;EAAC;EAAO;EADtC;GAAE,OAAO;GAAK,MAAM;GAAI;EAC8B;EAAS,GAAzC;AAErC,QACE,kBAAC,OAAD;EAAK,cAAW;EAAa,GAAI;EAAO,WAAW,EAAK,GAAW,kBAAkB;YACnF,kBAAC,MAAD;GAAI,WAAU;aACX,EAAc,KAAK,EAAE,UAAO,WAAQ,MAAU;IAC7C,IAAM,IAAS,MAAU,EAAc,SAAS,GAC1C,IAAS,KAAQ,CAAC;AAExB,WACE,kBAAC,MAAD;KACE,GAAK,KAAU,EAAE,gBAAgB,QAAQ;KACzC,WAAW,EACT,8BACA,GAAG,IAAQ,CAAC,GAAQ,EACpB;MACE,aAAa;OACZ,IAAc;MAChB,CACF;KACD,KAAK;KAIF,EAFF,IAAS,kBAAC,KAAD;KAAS;eAAO;KAAU,CAAA,GAAG,GACtC,CAAC,KAAU,kBAAC,GAAD;KAAM,WAAU;KAAO,MAAK;KAAU,OAAM;KAAS,CAAA,CAC9D;KAEP;GACC,CAAA;EACD,CAAA"}
|
package/ui/ChatButton.js
CHANGED
|
@@ -10,7 +10,7 @@ function o({ children: o = i, className: s, type: c = "button", "aria-label": l,
|
|
|
10
10
|
"aria-label": m,
|
|
11
11
|
tabIndex: u ?? 0,
|
|
12
12
|
...p,
|
|
13
|
-
className: e("chat-
|
|
13
|
+
className: e("chat-border grid grid-cols-[24px_0fr] transition-[grid-template-columns] justify-center items-center overflow-hidden cursor-pointer transition-all rounded-pill px-8 md:px-16 outline-none group/Chatbutton focus-visible:ring-8 focus-visible:ring-lavender/20 pointer-events-auto h-48 min-h-48 md:min-w-80 md:h-80 active:scale-[0.98]", d && "md:translate-x-[40px] focus-visible:translate-x-0", f === "hover" && "group/hoverChatbutton hover:grid-cols-[24px_1fr] hover:translate-x-0", f === "active" && "group/activeChatbutton active:grid-cols-[24px_1fr] active:translate-x-0", "focus-visible:grid-cols-[24px_1fr]", s),
|
|
14
14
|
children: [/* @__PURE__ */ n(t, {
|
|
15
15
|
name: "Sparkles",
|
|
16
16
|
className: e("chat-button-icon w-24 transition-all", d && "relative md:start-[calc(-16px)] group-focus-visible/Chatbutton:start-0 group-hover/hoverChatbutton:start-0 group-active/activeChatbutton:start-0")
|
package/ui/ChatButton.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatButton.js","names":[],"sources":["../../lib/ui/ChatButton.tsx"],"sourcesContent":["import { Icon } from '@clubmed/trident-icons';\nimport type { ButtonHTMLAttributes } from 'react';\nimport { twMerge } from './helpers/twMerge';\n\nconst DEFAULT_TEASER_COPY = 'Hello, Ask me a question';\nconst FALLBACK_ARIA_LABEL = 'Chat with your assistant';\n\nexport type ChatButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {\n offset?: boolean;\n unfoldOn?: 'hover' | 'active';\n};\n\nexport function ChatButton({\n children = DEFAULT_TEASER_COPY,\n className,\n type = 'button',\n 'aria-label': ariaLabel,\n tabIndex,\n offset,\n unfoldOn = 'hover',\n ...rest\n}: ChatButtonProps) {\n const label =\n ariaLabel ??\n (typeof children === 'string' && children.trim().length > 0\n ? children.trim()\n : FALLBACK_ARIA_LABEL);\n\n return (\n <button\n type={type}\n aria-label={label}\n tabIndex={tabIndex ?? 0}\n {...rest}\n className={twMerge(\n 'chat-
|
|
1
|
+
{"version":3,"file":"ChatButton.js","names":[],"sources":["../../lib/ui/ChatButton.tsx"],"sourcesContent":["import { Icon } from '@clubmed/trident-icons';\nimport type { ButtonHTMLAttributes } from 'react';\nimport { twMerge } from './helpers/twMerge';\n\nconst DEFAULT_TEASER_COPY = 'Hello, Ask me a question';\nconst FALLBACK_ARIA_LABEL = 'Chat with your assistant';\n\nexport type ChatButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {\n offset?: boolean;\n unfoldOn?: 'hover' | 'active';\n};\n\nexport function ChatButton({\n children = DEFAULT_TEASER_COPY,\n className,\n type = 'button',\n 'aria-label': ariaLabel,\n tabIndex,\n offset,\n unfoldOn = 'hover',\n ...rest\n}: ChatButtonProps) {\n const label =\n ariaLabel ??\n (typeof children === 'string' && children.trim().length > 0\n ? children.trim()\n : FALLBACK_ARIA_LABEL);\n\n return (\n <button\n type={type}\n aria-label={label}\n tabIndex={tabIndex ?? 0}\n {...rest}\n className={twMerge(\n 'chat-border grid grid-cols-[24px_0fr] transition-[grid-template-columns] justify-center items-center overflow-hidden cursor-pointer transition-all rounded-pill px-8 md:px-16 outline-none group/Chatbutton focus-visible:ring-8 focus-visible:ring-lavender/20 pointer-events-auto h-48 min-h-48 md:min-w-80 md:h-80 active:scale-[0.98]',\n offset && 'md:translate-x-[40px] focus-visible:translate-x-0',\n unfoldOn === 'hover' &&\n 'group/hoverChatbutton hover:grid-cols-[24px_1fr] hover:translate-x-0',\n unfoldOn === 'active' &&\n 'group/activeChatbutton active:grid-cols-[24px_1fr] active:translate-x-0',\n 'focus-visible:grid-cols-[24px_1fr]',\n className,\n )}\n >\n <Icon\n name=\"Sparkles\"\n className={twMerge(\n 'chat-button-icon w-24 transition-all',\n offset &&\n 'relative md:start-[calc(-16px)] group-focus-visible/Chatbutton:start-0 group-hover/hoverChatbutton:start-0 group-active/activeChatbutton:start-0',\n )}\n />\n <div className=\"grid grid-cols-[0fr] overflow-hidden opacity-0 transition-opacity group-hover/hoverChatbutton:opacity-100 group-focus-visible/Chatbutton:opacity-100 group-active/activeChatbutton:opacity-100 duration-500\">\n <span\n aria-hidden=\"true\"\n className=\"chat-button-text items-center ps-20 flex gap-10 whitespace-nowrap font-semibold text-b3 text-black\"\n >\n {children}\n <Icon name=\"ArrowTailRight\" width=\"24px\" />\n </span>\n </div>\n </button>\n );\n}\n"],"mappings":";;;;AAIA,IAAM,IAAsB,4BACtB,IAAsB;AAO5B,SAAgB,EAAW,EACzB,cAAW,GACX,cACA,UAAO,UACP,cAAc,GACd,aACA,WACA,cAAW,SACX,GAAG,KACe;CAClB,IAAM,IACJ,MACC,OAAO,KAAa,YAAY,EAAS,MAAM,CAAC,SAAS,IACtD,EAAS,MAAM,GACf;AAEN,QACE,kBAAC,UAAD;EACQ;EACN,cAAY;EACZ,UAAU,KAAY;EACtB,GAAI;EACJ,WAAW,EACT,6UACA,KAAU,qDACV,MAAa,WACX,wEACF,MAAa,YACX,2EACF,sCACA,EACD;YAdH,CAgBE,kBAAC,GAAD;GACE,MAAK;GACL,WAAW,EACT,wCACA,KACE,mJACH;GACD,CAAA,EACF,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,QAAD;IACE,eAAY;IACZ,WAAU;cAFZ,CAIG,GACD,kBAAC,GAAD;KAAM,MAAK;KAAiB,OAAM;KAAS,CAAA,CACtC;;GACH,CAAA,CACC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FormHTMLAttributes } from 'react';
|
|
2
|
+
export interface ChatInputProps extends FormHTMLAttributes<HTMLFormElement> {
|
|
3
|
+
value: string;
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
onValueChange: (value: string) => void;
|
|
6
|
+
onSubmit: () => void;
|
|
7
|
+
/** Fallback for when the browser does not support the Web Speech API */
|
|
8
|
+
onAudioClick?: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function ChatInput({ value, placeholder, onValueChange, onSubmit, onAudioClick, className, ...props }: ChatInputProps): import("react/jsx-runtime").JSX.Element;
|
package/ui/ChatInput.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Button as e } from "./buttons/Button.js";
|
|
3
|
+
import { twMerge as t } from "./helpers/twMerge.js";
|
|
4
|
+
import { useEffect as n, useRef as r, useState as i } from "react";
|
|
5
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
6
|
+
//#region lib/ui/ChatInput.tsx
|
|
7
|
+
var s = 12, c = 48, l = 8;
|
|
8
|
+
function u(e) {
|
|
9
|
+
let t = r(e);
|
|
10
|
+
return n(() => {
|
|
11
|
+
t.current = e;
|
|
12
|
+
}), t;
|
|
13
|
+
}
|
|
14
|
+
var d = 3e3;
|
|
15
|
+
function f(e) {
|
|
16
|
+
let [t, a] = i(!1), [o, s] = i(!1), c = r(null), l = r(null), f = r(!1), p = u(e);
|
|
17
|
+
function m() {
|
|
18
|
+
l.current && clearTimeout(l.current);
|
|
19
|
+
}
|
|
20
|
+
function h() {
|
|
21
|
+
m(), l.current = setTimeout(() => {
|
|
22
|
+
f.current = !1, s(!1), c.current?.stop();
|
|
23
|
+
}, d);
|
|
24
|
+
}
|
|
25
|
+
n(() => {
|
|
26
|
+
let e = window.webkitSpeechRecognition ?? window.SpeechRecognition;
|
|
27
|
+
if (!e) return;
|
|
28
|
+
a(!0);
|
|
29
|
+
let t = new e();
|
|
30
|
+
return t.continuous = !0, t.interimResults = !0, t.lang = navigator.language, t.onresult = (e) => {
|
|
31
|
+
let t = Array.from(e.results).map((e) => e[0].transcript).join("");
|
|
32
|
+
p.current(t), h();
|
|
33
|
+
}, t.onend = () => {
|
|
34
|
+
if (m(), f.current) try {
|
|
35
|
+
t.start();
|
|
36
|
+
} catch {}
|
|
37
|
+
else s(!1);
|
|
38
|
+
}, t.onerror = (e) => {
|
|
39
|
+
(e.error === "not-allowed" || e.error === "aborted") && (f.current = !1, s(!1), m());
|
|
40
|
+
}, c.current = t, () => {
|
|
41
|
+
t.abort(), m();
|
|
42
|
+
};
|
|
43
|
+
}, []);
|
|
44
|
+
function g() {
|
|
45
|
+
let e = c.current;
|
|
46
|
+
e && (f.current ? (f.current = !1, e.stop()) : (f.current = !0, s(!0), e.start(), h()));
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
isSupported: t,
|
|
50
|
+
isListening: o,
|
|
51
|
+
toggle: g
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function p(e) {
|
|
55
|
+
let t = r(null);
|
|
56
|
+
return n(() => {
|
|
57
|
+
let n = t.current;
|
|
58
|
+
if (!e) {
|
|
59
|
+
n && (n.style.height = "0px");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
let r, i = null, a = null;
|
|
63
|
+
return navigator.mediaDevices.getUserMedia({
|
|
64
|
+
audio: !0,
|
|
65
|
+
video: !1
|
|
66
|
+
}).then((e) => {
|
|
67
|
+
i = e, a = new AudioContext();
|
|
68
|
+
let n = a.createMediaStreamSource(i), o = a.createAnalyser();
|
|
69
|
+
o.fftSize = 256, n.connect(o);
|
|
70
|
+
let s = new Uint8Array(o.frequencyBinCount);
|
|
71
|
+
function c() {
|
|
72
|
+
o.getByteFrequencyData(s);
|
|
73
|
+
let e = s.reduce((e, t) => e + t, 0) / s.length, n = t.current;
|
|
74
|
+
n && (n.style.height = `${Math.sqrt(e / 255) * 10}px`), r = requestAnimationFrame(c);
|
|
75
|
+
}
|
|
76
|
+
r = requestAnimationFrame(c);
|
|
77
|
+
}), () => {
|
|
78
|
+
cancelAnimationFrame(r), i?.getTracks().forEach((e) => e.stop()), a?.close();
|
|
79
|
+
};
|
|
80
|
+
}, [e]), t;
|
|
81
|
+
}
|
|
82
|
+
function m({ value: n, placeholder: r = "Ask a question?", onValueChange: u, onSubmit: d, onAudioClick: m, className: h, ...g }) {
|
|
83
|
+
let [_, v] = i(!1), y = n.trim(), b = _ || y.length > 0, { isSupported: x, isListening: S, toggle: C } = f(u), w = p(S), T = x || !!m, E = T ? c + l + c : c;
|
|
84
|
+
function D() {
|
|
85
|
+
x ? C() : m?.();
|
|
86
|
+
}
|
|
87
|
+
function O(e) {
|
|
88
|
+
e.preventDefault(), y && d();
|
|
89
|
+
}
|
|
90
|
+
return /* @__PURE__ */ o("form", {
|
|
91
|
+
onSubmit: O,
|
|
92
|
+
onMouseDown: (e) => {
|
|
93
|
+
e.target.tagName !== "TEXTAREA" && e.preventDefault();
|
|
94
|
+
},
|
|
95
|
+
...g,
|
|
96
|
+
style: { paddingBottom: b ? s + l + c : s },
|
|
97
|
+
className: t("relative flex items-start overflow-hidden bg-white border border-light-grey p-12 w-full", "transition-[padding-bottom,border-radius] duration-250 ease-out rounded-b-[24]", b ? "rounded-32" : "rounded-[40]", h),
|
|
98
|
+
children: [/* @__PURE__ */ a("textarea", {
|
|
99
|
+
rows: b ? 3 : 1,
|
|
100
|
+
value: n,
|
|
101
|
+
onChange: (e) => u(e.target.value),
|
|
102
|
+
onKeyDown: (e) => {
|
|
103
|
+
e.key === "Enter" && (e.metaKey || e.ctrlKey) && y && (e.preventDefault(), d());
|
|
104
|
+
},
|
|
105
|
+
onFocus: () => v(!0),
|
|
106
|
+
onBlur: () => v(!1),
|
|
107
|
+
placeholder: r,
|
|
108
|
+
"aria-label": r,
|
|
109
|
+
style: { marginInlineEnd: b ? 0 : E + l },
|
|
110
|
+
className: t("scrollbar-hidden bg-transparent text-b3 py-12 outline-none resize-none w-full px-20 border border-middleGrey", "transition-[margin-inline-end,border-radius] duration-250 ease-out", b ? "text-black rounded-[20]" : "text-darkGrey placeholder:text-darkGrey rounded-[40]")
|
|
111
|
+
}), /* @__PURE__ */ o("div", {
|
|
112
|
+
style: {
|
|
113
|
+
insetInlineStart: b ? `calc(50% - ${E / 2}px)` : `calc(100% - ${s + E}px)`,
|
|
114
|
+
bottom: s
|
|
115
|
+
},
|
|
116
|
+
className: "absolute flex gap-8 items-center transition-[inset-inline-start] duration-250 ease-out",
|
|
117
|
+
children: [/* @__PURE__ */ a(e, {
|
|
118
|
+
type: "submit",
|
|
119
|
+
"aria-label": "Send message",
|
|
120
|
+
disabled: !y,
|
|
121
|
+
variant: "circle",
|
|
122
|
+
color: "saffron",
|
|
123
|
+
icon: "ArrowTailRight",
|
|
124
|
+
iconWidth: "24px"
|
|
125
|
+
}), T && /* @__PURE__ */ a(e, {
|
|
126
|
+
type: "button",
|
|
127
|
+
"aria-label": S ? "Stop recording" : "Use microphone",
|
|
128
|
+
"aria-pressed": S,
|
|
129
|
+
onClick: D,
|
|
130
|
+
variant: "circle",
|
|
131
|
+
theme: S ? "solid" : "outline",
|
|
132
|
+
color: S ? "sienna" : "black",
|
|
133
|
+
icon: "Audio",
|
|
134
|
+
iconWidth: "24px",
|
|
135
|
+
className: t("relative", S && "animate-pulse", !S && "border-lightGrey"),
|
|
136
|
+
children: /* @__PURE__ */ a("span", {
|
|
137
|
+
ref: w,
|
|
138
|
+
"aria-hidden": "true",
|
|
139
|
+
className: "bg-current absolute w-5 bottom-21",
|
|
140
|
+
style: { height: "0px" }
|
|
141
|
+
})
|
|
142
|
+
})]
|
|
143
|
+
})]
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
export { m as ChatInput };
|
|
148
|
+
|
|
149
|
+
//# sourceMappingURL=ChatInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatInput.js","names":[],"sources":["../../lib/ui/ChatInput.tsx"],"sourcesContent":["/// <reference types=\"@types/dom-speech-recognition\" />\n'use client';\n\nimport type { FormHTMLAttributes } from 'react';\nimport { useEffect, useRef, useState } from 'react';\nimport { Button } from './buttons/Button';\nimport { twMerge } from './helpers/twMerge';\n\nexport interface ChatInputProps extends FormHTMLAttributes<HTMLFormElement> {\n value: string;\n placeholder?: string;\n onValueChange: (value: string) => void;\n onSubmit: () => void;\n /** Fallback for when the browser does not support the Web Speech API */\n onAudioClick?: () => void;\n}\n\nconst FORM_PAD = 12; // matches p-12\nconst BTN_SIZE = 48; // matches size-48\nconst BTN_GAP = 8; // matches gap-8\n\n// Keep onValueChange stable inside the recognition handler without re-creating the SR instance\nfunction useCallbackRef<T extends (...args: never[]) => unknown>(fn: T) {\n const ref = useRef(fn);\n useEffect(() => {\n ref.current = fn;\n });\n return ref;\n}\n\nconst SILENCE_TIMEOUT_MS = 3_000;\n\nfunction useSpeechRecognition(onValueChange: (v: string) => void) {\n const [isSupported, setIsSupported] = useState(false);\n const [isListening, setIsListening] = useState(false);\n const recognitionRef = useRef<SpeechRecognition | null>(null);\n const silenceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const isListeningRef = useRef(false);\n const onValueChangeRef = useCallbackRef(onValueChange);\n\n function clearSilenceTimer() {\n if (silenceTimerRef.current) clearTimeout(silenceTimerRef.current);\n }\n\n function resetSilenceTimer() {\n clearSilenceTimer();\n silenceTimerRef.current = setTimeout(() => {\n isListeningRef.current = false;\n setIsListening(false);\n recognitionRef.current?.stop();\n }, SILENCE_TIMEOUT_MS);\n }\n\n useEffect(() => {\n const SR: typeof SpeechRecognition | undefined =\n (window as Window & { webkitSpeechRecognition?: typeof SpeechRecognition })\n .webkitSpeechRecognition ?? window.SpeechRecognition;\n if (!SR) return;\n\n setIsSupported(true);\n\n const recognition = new SR();\n recognition.continuous = true;\n recognition.interimResults = true;\n recognition.lang = navigator.language;\n\n recognition.onresult = (event: SpeechRecognitionEvent) => {\n const transcript = Array.from(event.results)\n .map((r: SpeechRecognitionResult) => r[0].transcript)\n .join('');\n onValueChangeRef.current(transcript);\n resetSilenceTimer();\n };\n\n recognition.onend = () => {\n clearSilenceTimer();\n // Restart automatically if the user hasn't manually stopped (browser cut us off)\n if (isListeningRef.current) {\n try {\n recognition.start();\n } catch {\n /* already starting */\n }\n } else {\n setIsListening(false);\n }\n };\n\n recognition.onerror = (e: SpeechRecognitionErrorEvent) => {\n if (e.error === 'not-allowed' || e.error === 'aborted') {\n isListeningRef.current = false;\n setIsListening(false);\n clearSilenceTimer();\n }\n };\n\n recognitionRef.current = recognition;\n return () => {\n recognition.abort();\n clearSilenceTimer();\n };\n // SR detection and instance creation only needs to run once on mount\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n function toggle() {\n const recognition = recognitionRef.current;\n if (!recognition) return;\n if (isListeningRef.current) {\n isListeningRef.current = false;\n recognition.stop();\n } else {\n isListeningRef.current = true;\n setIsListening(true);\n recognition.start();\n resetSilenceTimer();\n }\n }\n\n return { isSupported, isListening, toggle };\n}\n\n/**\n * Drives the audio-level indicator by writing directly to the DOM element's\n * style — avoids triggering a React re-render on every animation frame\n * (rule: rerender-use-ref-transient-values).\n *\n * Returns a ref to attach to the indicator <span>.\n */\nfunction useAudioLevel(isListening: boolean) {\n const indicatorRef = useRef<HTMLSpanElement>(null);\n\n useEffect(() => {\n const el = indicatorRef.current;\n\n if (!isListening) {\n if (el) el.style.height = '0px';\n return;\n }\n\n let animId: number;\n let stream: MediaStream | null = null;\n let ctx: AudioContext | null = null;\n\n navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then((s) => {\n stream = s;\n ctx = new AudioContext();\n const source = ctx.createMediaStreamSource(stream);\n const analyser = ctx.createAnalyser();\n analyser.fftSize = 256;\n source.connect(analyser);\n const data = new Uint8Array(analyser.frequencyBinCount);\n\n function tick() {\n analyser.getByteFrequencyData(data);\n const avg = data.reduce((sum, v) => sum + v, 0) / data.length;\n // Square-root curve so quiet sounds register more visibly\n const node = indicatorRef.current;\n if (node) node.style.height = `${Math.sqrt(avg / 255) * 10}px`;\n animId = requestAnimationFrame(tick);\n }\n animId = requestAnimationFrame(tick);\n });\n\n return () => {\n cancelAnimationFrame(animId);\n stream?.getTracks().forEach((t) => t.stop());\n ctx?.close();\n };\n }, [isListening]);\n\n return indicatorRef;\n}\n\nexport function ChatInput({\n value,\n placeholder = 'Ask a question?',\n onValueChange,\n onSubmit,\n onAudioClick,\n className,\n ...props\n}: ChatInputProps) {\n const [isFocused, setIsFocused] = useState(false);\n const trimmed = value.trim();\n const isOpen = isFocused || trimmed.length > 0;\n const { isSupported, isListening, toggle } = useSpeechRecognition(onValueChange);\n const audioIndicatorRef = useAudioLevel(isListening);\n\n const showAudio = isSupported || !!onAudioClick;\n const btnW = showAudio ? BTN_SIZE + BTN_GAP + BTN_SIZE : BTN_SIZE;\n\n function handleAudioClick() {\n if (isSupported) {\n toggle();\n } else {\n onAudioClick?.();\n }\n }\n\n function handleSubmit(e: React.FormEvent) {\n e.preventDefault();\n if (trimmed) onSubmit();\n }\n\n return (\n <form\n onSubmit={handleSubmit}\n onMouseDown={(e) => {\n if ((e.target as HTMLElement).tagName !== 'TEXTAREA') e.preventDefault();\n }}\n {...props}\n style={{\n paddingBottom: isOpen ? FORM_PAD + BTN_GAP + BTN_SIZE : FORM_PAD,\n }}\n className={twMerge(\n 'relative flex items-start overflow-hidden bg-white border border-light-grey p-12 w-full',\n 'transition-[padding-bottom,border-radius] duration-250 ease-out rounded-b-[24]',\n isOpen ? 'rounded-32' : 'rounded-[40]',\n className,\n )}\n >\n <textarea\n rows={isOpen ? 3 : 1}\n value={value}\n onChange={(e) => onValueChange(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey) && trimmed) {\n e.preventDefault();\n onSubmit();\n }\n }}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n placeholder={placeholder}\n aria-label={placeholder}\n style={{\n marginInlineEnd: isOpen ? 0 : btnW + BTN_GAP,\n }}\n className={twMerge(\n 'scrollbar-hidden bg-transparent text-b3 py-12 outline-none resize-none w-full px-20 border border-middleGrey',\n 'transition-[margin-inline-end,border-radius] duration-250 ease-out',\n isOpen\n ? 'text-black rounded-[20]'\n : 'text-darkGrey placeholder:text-darkGrey rounded-[40]',\n )}\n />\n\n <div\n style={{\n insetInlineStart: isOpen\n ? `calc(50% - ${btnW / 2}px)`\n : `calc(100% - ${FORM_PAD + btnW}px)`,\n bottom: FORM_PAD,\n }}\n className=\"absolute flex gap-8 items-center transition-[inset-inline-start] duration-250 ease-out\"\n >\n <Button\n type=\"submit\"\n aria-label=\"Send message\"\n disabled={!trimmed}\n variant=\"circle\"\n color=\"saffron\"\n icon=\"ArrowTailRight\"\n iconWidth=\"24px\"\n />\n {showAudio && (\n <Button\n type=\"button\"\n aria-label={isListening ? 'Stop recording' : 'Use microphone'}\n aria-pressed={isListening}\n onClick={handleAudioClick}\n variant=\"circle\"\n theme={isListening ? 'solid' : 'outline'}\n color={isListening ? 'sienna' : 'black'}\n icon=\"Audio\"\n iconWidth=\"24px\"\n className={twMerge(\n 'relative',\n isListening && 'animate-pulse',\n !isListening && 'border-lightGrey',\n )}\n >\n <span\n ref={audioIndicatorRef}\n aria-hidden=\"true\"\n className=\"bg-current absolute w-5 bottom-21\"\n style={{ height: '0px' }}\n />\n </Button>\n )}\n </div>\n </form>\n );\n}\n"],"mappings":"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"}
|