@clubmed/trident-ui 2.0.0-beta.64 → 2.0.0-beta.65
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/Backdrop.js +1 -1
- package/chunks/Backdrop.js.map +1 -1
- package/chunks/DateField.js +2 -2
- package/chunks/DateField.js.map +1 -1
- package/package.json +1 -1
- package/styles/ui/controls.css +3 -6
- package/styles/ui/range.css +5 -0
- package/ui/AdvancedToast.js +2 -2
- package/ui/AdvancedToast.js.map +1 -1
- package/ui/Avatar.js +1 -1
- package/ui/Avatar.js.map +1 -1
- package/ui/BasicToast.js +2 -2
- package/ui/BasicToast.js.map +1 -1
- package/ui/Breadcrumb.js +1 -0
- package/ui/Breadcrumb.js.map +1 -1
- package/ui/ChatButton.js +1 -1
- package/ui/ChatButton.js.map +1 -1
- package/ui/ChatInput.js +1 -1
- package/ui/ChatInput.js.map +1 -1
- package/ui/ChatWindow.js +2 -2
- package/ui/ChatWindow.js.map +1 -1
- package/ui/ChatWindowCsatPrompt.js +1 -1
- package/ui/ChatWindowCsatPrompt.js.map +1 -1
- package/ui/ChoiceExpander.js +1 -1
- package/ui/ChoiceExpander.js.map +1 -1
- package/ui/ExpandableText.js +1 -1
- package/ui/ExpandableText.js.map +1 -1
- package/ui/HamburgerIcon.js +1 -1
- package/ui/HamburgerIcon.js.map +1 -1
- package/ui/Link.js +1 -1
- package/ui/Link.js.map +1 -1
- package/ui/PageNavigation.js +1 -1
- package/ui/PageNavigation.js.map +1 -1
- package/ui/Pagination.js +2 -2
- package/ui/Pagination.js.map +1 -1
- package/ui/SidebarLayout.js +4 -4
- package/ui/SidebarLayout.js.map +1 -1
- package/ui/Toggle.js +1 -1
- package/ui/Toggle.js.map +1 -1
- package/ui/buttons/Button.type.js +1 -1
- package/ui/buttons/Button.type.js.map +1 -1
- package/ui/forms/CheckboxSelect.js +1 -1
- package/ui/forms/CheckboxSelect.js.map +1 -1
- package/ui/forms/FloatingLabelTextField.js +2 -2
- package/ui/forms/FloatingLabelTextField.js.map +1 -1
- package/ui/forms/NumberField.js +1 -1
- package/ui/forms/NumberField.js.map +1 -1
- package/ui/forms/PhoneFieldFullInput.js +1 -1
- package/ui/forms/PhoneFieldFullInput.js.map +1 -1
- package/ui/forms/PhoneFieldSplitInput.js +2 -2
- package/ui/forms/PhoneFieldSplitInput.js.map +1 -1
- package/ui/forms/Select.js +1 -1
- package/ui/forms/Select.js.map +1 -1
- package/ui/forms/TextField.js +3 -3
- package/ui/forms/TextField.js.map +1 -1
- package/ui/forms/checkboxes/Checkbox.js +1 -1
- package/ui/forms/checkboxes/Checkbox.js.map +1 -1
- package/ui/forms/password/Password.js +2 -2
- package/ui/forms/password/Password.js.map +1 -1
- package/ui/journey-card/JourneyDetails.js +2 -2
- package/ui/journey-card/JourneyDetails.js.map +1 -1
package/chunks/Backdrop.js
CHANGED
|
@@ -87,7 +87,7 @@ var c = 5, l = 6, u = [
|
|
|
87
87
|
className: e(i, "fixed inset-0 isolate items-center"),
|
|
88
88
|
children: [/* @__PURE__ */ o("button", {
|
|
89
89
|
"aria-hidden": "true",
|
|
90
|
-
className: e("absolute inset-0 -z-1 transition-all duration-500", {
|
|
90
|
+
className: e("absolute inset-0 -z-1 transition-all duration-500 outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2", {
|
|
91
91
|
"bg-white/80 backdrop-blur": !g,
|
|
92
92
|
"bg-white/0 backdrop-blur-none": g,
|
|
93
93
|
"-translate-x-full": u && (m === "preEnter" || m === "entering"),
|
package/chunks/Backdrop.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Backdrop.js","names":[],"sources":["../../../../node_modules/.pnpm/react-transition-state@2.3.3_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/react-transition-state/dist/esm/utils.mjs","../../../../node_modules/.pnpm/react-transition-state@2.3.3_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/react-transition-state/dist/esm/useTransitionState.mjs","../../lib/ui/Backdrop.tsx"],"sourcesContent":["//#region src/utils.ts\nconst PRE_ENTER = 0;\nconst ENTERING = 1;\nconst ENTERED = 2;\nconst PRE_EXIT = 3;\nconst EXITING = 4;\nconst EXITED = 5;\nconst UNMOUNTED = 6;\nconst STATUS = [\n\t\"preEnter\",\n\t\"entering\",\n\t\"entered\",\n\t\"preExit\",\n\t\"exiting\",\n\t\"exited\",\n\t\"unmounted\"\n];\nconst getState = (status) => ({\n\t_s: status,\n\tstatus: STATUS[status],\n\tisEnter: status < PRE_EXIT,\n\tisMounted: status !== UNMOUNTED,\n\tisResolved: status === ENTERED || status > EXITING\n});\nconst startOrEnd = (unmounted) => unmounted ? UNMOUNTED : EXITED;\nconst getEndStatus = (status, unmountOnExit) => {\n\tswitch (status) {\n\t\tcase ENTERING:\n\t\tcase PRE_ENTER: return ENTERED;\n\t\tcase EXITING:\n\t\tcase PRE_EXIT: return startOrEnd(unmountOnExit);\n\t}\n};\nconst getTimeout = (timeout) => typeof timeout === \"object\" ? [timeout.enter, timeout.exit] : [timeout, timeout];\nconst _setTimeout = (...args) => setTimeout(...args);\nconst nextTick = (transitState, status) => _setTimeout(() => {\n\tisNaN(document.body.offsetTop) || transitState(status + 1);\n}, 0);\n\n//#endregion\nexport { ENTERED, ENTERING, EXITING, PRE_ENTER, PRE_EXIT, _setTimeout, getEndStatus, getState, getTimeout, nextTick, startOrEnd };","import { ENTERED, ENTERING, EXITING, PRE_ENTER, PRE_EXIT, _setTimeout, getEndStatus, getState, getTimeout, nextTick, startOrEnd } from \"./utils.mjs\";\nimport { useCallback, useRef, useState } from \"react\";\n\n//#region src/useTransitionState.ts\nconst updateState = (status, setState, latestState, timeoutId, onChange) => {\n\tclearTimeout(timeoutId.current);\n\tconst state = getState(status);\n\tsetState(state);\n\tlatestState.current = state;\n\tonChange && onChange({ current: state });\n};\nconst useTransitionState = ({ enter = true, exit = true, preEnter, preExit, timeout, initialEntered, mountOnEnter, unmountOnExit, onStateChange: onChange } = {}) => {\n\tconst [state, setState] = useState(() => getState(initialEntered ? ENTERED : startOrEnd(mountOnEnter)));\n\tconst latestState = useRef(state);\n\tconst timeoutId = useRef(0);\n\tconst [enterTimeout, exitTimeout] = getTimeout(timeout);\n\tconst endTransition = useCallback(() => {\n\t\tconst status = getEndStatus(latestState.current._s, unmountOnExit);\n\t\tstatus && updateState(status, setState, latestState, timeoutId, onChange);\n\t}, [onChange, unmountOnExit]);\n\treturn [\n\t\tstate,\n\t\tuseCallback((toEnter) => {\n\t\t\tconst transitState = (status) => {\n\t\t\t\tupdateState(status, setState, latestState, timeoutId, onChange);\n\t\t\t\tswitch (status) {\n\t\t\t\t\tcase ENTERING:\n\t\t\t\t\t\tif (enterTimeout >= 0) timeoutId.current = _setTimeout(endTransition, enterTimeout);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase EXITING:\n\t\t\t\t\t\tif (exitTimeout >= 0) timeoutId.current = _setTimeout(endTransition, exitTimeout);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase PRE_ENTER:\n\t\t\t\t\tcase PRE_EXIT:\n\t\t\t\t\t\ttimeoutId.current = nextTick(transitState, status);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst enterStage = latestState.current.isEnter;\n\t\t\tif (typeof toEnter !== \"boolean\") toEnter = !enterStage;\n\t\t\tif (toEnter) !enterStage && transitState(enter ? preEnter ? PRE_ENTER : ENTERING : ENTERED);\n\t\t\telse enterStage && transitState(exit ? preExit ? PRE_EXIT : EXITING : startOrEnd(unmountOnExit));\n\t\t}, [\n\t\t\tendTransition,\n\t\t\tonChange,\n\t\t\tenter,\n\t\t\texit,\n\t\t\tpreEnter,\n\t\t\tpreExit,\n\t\t\tenterTimeout,\n\t\t\texitTimeout,\n\t\t\tunmountOnExit\n\t\t]),\n\t\tendTransition\n\t];\n};\n\n//#endregion\nexport { useTransitionState };","import {\n type ComponentProps,\n type FunctionComponent,\n type MouseEvent,\n type PropsWithChildren,\n useEffect,\n} from 'react';\nimport clsx from 'clsx';\nimport { useTransitionState } from 'react-transition-state';\nimport { Portal } from './Portal';\n\nexport interface BackdropProps extends ComponentProps<'div'> {\n /**\n * On close\n * @param event\n */\n onClose?: (event: MouseEvent<HTMLButtonElement>) => void;\n /**\n * Is visible\n */\n isVisible?: boolean;\n /**\n * Sweep Mode™\n * (makes it appear from the sides instead of from everywhere)\n */\n sweep?: boolean;\n /**\n * Target ID for the Portal component\n */\n target?: ComponentProps<typeof Portal>['target'];\n}\n\nexport const Backdrop: FunctionComponent<PropsWithChildren<BackdropProps>> = ({\n children,\n className = 'flex justify-center',\n onClose,\n isVisible,\n target = 'backdrop',\n sweep = false,\n ...attrs\n}) => {\n const [state, toggle] = useTransitionState({\n timeout: { enter: 0, exit: 500 },\n mountOnEnter: true,\n unmountOnExit: true,\n preEnter: true,\n });\n\n const { status, isMounted } = state;\n\n useEffect(() => {\n toggle(isVisible);\n }, [isVisible, toggle]);\n\n const animated = status === 'preEnter' || status === 'exiting';\n\n return (\n <Portal target={target}>\n {isMounted && (\n <div\n {...attrs}\n role=\"presentation\"\n className={clsx(className, 'fixed inset-0 isolate items-center')}\n >\n <button\n aria-hidden=\"true\"\n className={clsx('absolute inset-0 -z-1 transition-all duration-500'
|
|
1
|
+
{"version":3,"file":"Backdrop.js","names":[],"sources":["../../../../node_modules/.pnpm/react-transition-state@2.3.3_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/react-transition-state/dist/esm/utils.mjs","../../../../node_modules/.pnpm/react-transition-state@2.3.3_react-dom@19.2.4_react@19.2.4__react@19.2.4/node_modules/react-transition-state/dist/esm/useTransitionState.mjs","../../lib/ui/Backdrop.tsx"],"sourcesContent":["//#region src/utils.ts\nconst PRE_ENTER = 0;\nconst ENTERING = 1;\nconst ENTERED = 2;\nconst PRE_EXIT = 3;\nconst EXITING = 4;\nconst EXITED = 5;\nconst UNMOUNTED = 6;\nconst STATUS = [\n\t\"preEnter\",\n\t\"entering\",\n\t\"entered\",\n\t\"preExit\",\n\t\"exiting\",\n\t\"exited\",\n\t\"unmounted\"\n];\nconst getState = (status) => ({\n\t_s: status,\n\tstatus: STATUS[status],\n\tisEnter: status < PRE_EXIT,\n\tisMounted: status !== UNMOUNTED,\n\tisResolved: status === ENTERED || status > EXITING\n});\nconst startOrEnd = (unmounted) => unmounted ? UNMOUNTED : EXITED;\nconst getEndStatus = (status, unmountOnExit) => {\n\tswitch (status) {\n\t\tcase ENTERING:\n\t\tcase PRE_ENTER: return ENTERED;\n\t\tcase EXITING:\n\t\tcase PRE_EXIT: return startOrEnd(unmountOnExit);\n\t}\n};\nconst getTimeout = (timeout) => typeof timeout === \"object\" ? [timeout.enter, timeout.exit] : [timeout, timeout];\nconst _setTimeout = (...args) => setTimeout(...args);\nconst nextTick = (transitState, status) => _setTimeout(() => {\n\tisNaN(document.body.offsetTop) || transitState(status + 1);\n}, 0);\n\n//#endregion\nexport { ENTERED, ENTERING, EXITING, PRE_ENTER, PRE_EXIT, _setTimeout, getEndStatus, getState, getTimeout, nextTick, startOrEnd };","import { ENTERED, ENTERING, EXITING, PRE_ENTER, PRE_EXIT, _setTimeout, getEndStatus, getState, getTimeout, nextTick, startOrEnd } from \"./utils.mjs\";\nimport { useCallback, useRef, useState } from \"react\";\n\n//#region src/useTransitionState.ts\nconst updateState = (status, setState, latestState, timeoutId, onChange) => {\n\tclearTimeout(timeoutId.current);\n\tconst state = getState(status);\n\tsetState(state);\n\tlatestState.current = state;\n\tonChange && onChange({ current: state });\n};\nconst useTransitionState = ({ enter = true, exit = true, preEnter, preExit, timeout, initialEntered, mountOnEnter, unmountOnExit, onStateChange: onChange } = {}) => {\n\tconst [state, setState] = useState(() => getState(initialEntered ? ENTERED : startOrEnd(mountOnEnter)));\n\tconst latestState = useRef(state);\n\tconst timeoutId = useRef(0);\n\tconst [enterTimeout, exitTimeout] = getTimeout(timeout);\n\tconst endTransition = useCallback(() => {\n\t\tconst status = getEndStatus(latestState.current._s, unmountOnExit);\n\t\tstatus && updateState(status, setState, latestState, timeoutId, onChange);\n\t}, [onChange, unmountOnExit]);\n\treturn [\n\t\tstate,\n\t\tuseCallback((toEnter) => {\n\t\t\tconst transitState = (status) => {\n\t\t\t\tupdateState(status, setState, latestState, timeoutId, onChange);\n\t\t\t\tswitch (status) {\n\t\t\t\t\tcase ENTERING:\n\t\t\t\t\t\tif (enterTimeout >= 0) timeoutId.current = _setTimeout(endTransition, enterTimeout);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase EXITING:\n\t\t\t\t\t\tif (exitTimeout >= 0) timeoutId.current = _setTimeout(endTransition, exitTimeout);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase PRE_ENTER:\n\t\t\t\t\tcase PRE_EXIT:\n\t\t\t\t\t\ttimeoutId.current = nextTick(transitState, status);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst enterStage = latestState.current.isEnter;\n\t\t\tif (typeof toEnter !== \"boolean\") toEnter = !enterStage;\n\t\t\tif (toEnter) !enterStage && transitState(enter ? preEnter ? PRE_ENTER : ENTERING : ENTERED);\n\t\t\telse enterStage && transitState(exit ? preExit ? PRE_EXIT : EXITING : startOrEnd(unmountOnExit));\n\t\t}, [\n\t\t\tendTransition,\n\t\t\tonChange,\n\t\t\tenter,\n\t\t\texit,\n\t\t\tpreEnter,\n\t\t\tpreExit,\n\t\t\tenterTimeout,\n\t\t\texitTimeout,\n\t\t\tunmountOnExit\n\t\t]),\n\t\tendTransition\n\t];\n};\n\n//#endregion\nexport { useTransitionState };","import {\n type ComponentProps,\n type FunctionComponent,\n type MouseEvent,\n type PropsWithChildren,\n useEffect,\n} from 'react';\nimport clsx from 'clsx';\nimport { useTransitionState } from 'react-transition-state';\nimport { Portal } from './Portal';\n\nexport interface BackdropProps extends ComponentProps<'div'> {\n /**\n * On close\n * @param event\n */\n onClose?: (event: MouseEvent<HTMLButtonElement>) => void;\n /**\n * Is visible\n */\n isVisible?: boolean;\n /**\n * Sweep Mode™\n * (makes it appear from the sides instead of from everywhere)\n */\n sweep?: boolean;\n /**\n * Target ID for the Portal component\n */\n target?: ComponentProps<typeof Portal>['target'];\n}\n\nexport const Backdrop: FunctionComponent<PropsWithChildren<BackdropProps>> = ({\n children,\n className = 'flex justify-center',\n onClose,\n isVisible,\n target = 'backdrop',\n sweep = false,\n ...attrs\n}) => {\n const [state, toggle] = useTransitionState({\n timeout: { enter: 0, exit: 500 },\n mountOnEnter: true,\n unmountOnExit: true,\n preEnter: true,\n });\n\n const { status, isMounted } = state;\n\n useEffect(() => {\n toggle(isVisible);\n }, [isVisible, toggle]);\n\n const animated = status === 'preEnter' || status === 'exiting';\n\n return (\n <Portal target={target}>\n {isMounted && (\n <div\n {...attrs}\n role=\"presentation\"\n className={clsx(className, 'fixed inset-0 isolate items-center')}\n >\n <button\n aria-hidden=\"true\"\n className={clsx(\n 'absolute inset-0 -z-1 transition-all duration-500 outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2',\n {\n 'bg-white/80 backdrop-blur': !animated,\n 'bg-white/0 backdrop-blur-none': animated,\n '-translate-x-full': sweep && (status === 'preEnter' || status === 'entering'),\n 'translate-x-full': sweep && status === 'exiting',\n },\n )}\n onClick={onClose}\n type=\"button\"\n />\n {children}\n </div>\n )}\n </Portal>\n );\n};\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
|
package/chunks/DateField.js
CHANGED
|
@@ -190,7 +190,7 @@ var z = (e) => e ? typeof e == "string" ? e : s(new Date(e)) ? l(new Date(e), {
|
|
|
190
190
|
children: [/* @__PURE__ */ g("input", {
|
|
191
191
|
...P,
|
|
192
192
|
type: "date",
|
|
193
|
-
className: e("text-b3 rounded-pill date-field min-h-[50px] w-full border px-20 py-12 ps-[52px] font-normal outline-none", {
|
|
193
|
+
className: e("text-b3 rounded-pill date-field min-h-[50px] w-full border px-20 py-12 ps-[52px] font-normal outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2", {
|
|
194
194
|
"border-middleGrey focus:border-black active:border-black": R === "default",
|
|
195
195
|
"pe-[52px]": R === "error" || R === "success",
|
|
196
196
|
"bg-pearl border-middleGrey": T,
|
|
@@ -216,7 +216,7 @@ var z = (e) => e ? typeof e == "string" ? e : s(new Date(e)) ? l(new Date(e), {
|
|
|
216
216
|
"text-green": !T && R === "success"
|
|
217
217
|
}),
|
|
218
218
|
children: [/* @__PURE__ */ _("button", {
|
|
219
|
-
className: "pointer-events-auto leading-none",
|
|
219
|
+
className: "pointer-events-auto leading-none outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2",
|
|
220
220
|
onClick: K,
|
|
221
221
|
children: [/* @__PURE__ */ g(h, {
|
|
222
222
|
name: S,
|
package/chunks/DateField.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DateField.js","names":[],"sources":["../../../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/differenceInMilliseconds.js","../../../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/parseISO.js","../../lib/ui/forms/DateField.tsx"],"sourcesContent":["import { toDate } from \"./toDate.js\";\n\n/**\n * @name differenceInMilliseconds\n * @category Millisecond Helpers\n * @summary Get the number of milliseconds between the given dates.\n *\n * @description\n * Get the number of milliseconds between the given dates.\n *\n * @param laterDate - The later date\n * @param earlierDate - The earlier date\n *\n * @returns The number of milliseconds\n *\n * @example\n * // How many milliseconds are between\n * // 2 July 2014 12:30:20.600 and 2 July 2014 12:30:21.700?\n * const result = differenceInMilliseconds(\n * new Date(2014, 6, 2, 12, 30, 21, 700),\n * new Date(2014, 6, 2, 12, 30, 20, 600)\n * )\n * //=> 1100\n */\nexport function differenceInMilliseconds(laterDate, earlierDate) {\n return +toDate(laterDate) - +toDate(earlierDate);\n}\n\n// Fallback for modularized imports:\nexport default differenceInMilliseconds;\n","import {\n millisecondsInHour,\n millisecondsInMinute,\n} from \"./constants.js\";\nimport { constructFrom } from \"./constructFrom.js\";\nimport { toDate } from \"./toDate.js\";\n\n/**\n * The {@link parseISO} function options.\n */\n\n/**\n * @name parseISO\n * @category Common Helpers\n * @summary Parse ISO string\n *\n * @description\n * Parse the given string in ISO 8601 format and return an instance of Date.\n *\n * Function accepts complete ISO 8601 formats as well as partial implementations.\n * ISO 8601: http://en.wikipedia.org/wiki/ISO_8601\n *\n * If the argument isn't a string, the function cannot parse the string or\n * the values are invalid, it returns Invalid Date.\n *\n * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).\n * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments.\n *\n * @param argument - The value to convert\n * @param options - An object with options\n *\n * @returns The parsed date in the local time zone\n *\n * @example\n * // Convert string '2014-02-11T11:30:30' to date:\n * const result = parseISO('2014-02-11T11:30:30')\n * //=> Tue Feb 11 2014 11:30:30\n *\n * @example\n * // Convert string '+02014101' to date,\n * // if the additional number of digits in the extended year format is 1:\n * const result = parseISO('+02014101', { additionalDigits: 1 })\n * //=> Fri Apr 11 2014 00:00:00\n */\nexport function parseISO(argument, options) {\n const invalidDate = () => constructFrom(options?.in, NaN);\n\n const additionalDigits = options?.additionalDigits ?? 2;\n const dateStrings = splitDateString(argument);\n\n let date;\n if (dateStrings.date) {\n const parseYearResult = parseYear(dateStrings.date, additionalDigits);\n date = parseDate(parseYearResult.restDateString, parseYearResult.year);\n }\n\n if (!date || isNaN(+date)) return invalidDate();\n\n const timestamp = +date;\n let time = 0;\n let offset;\n\n if (dateStrings.time) {\n time = parseTime(dateStrings.time);\n if (isNaN(time)) return invalidDate();\n }\n\n if (dateStrings.timezone) {\n offset = parseTimezone(dateStrings.timezone);\n if (isNaN(offset)) return invalidDate();\n } else {\n const tmpDate = new Date(timestamp + time);\n const result = toDate(0, options?.in);\n result.setFullYear(\n tmpDate.getUTCFullYear(),\n tmpDate.getUTCMonth(),\n tmpDate.getUTCDate(),\n );\n result.setHours(\n tmpDate.getUTCHours(),\n tmpDate.getUTCMinutes(),\n tmpDate.getUTCSeconds(),\n tmpDate.getUTCMilliseconds(),\n );\n return result;\n }\n\n return toDate(timestamp + time + offset, options?.in);\n}\n\nconst patterns = {\n dateTimeDelimiter: /[T ]/,\n timeZoneDelimiter: /[Z ]/i,\n timezone: /([Z+-].*)$/,\n};\n\nconst dateRegex =\n /^-?(?:(\\d{3})|(\\d{2})(?:-?(\\d{2}))?|W(\\d{2})(?:-?(\\d{1}))?|)$/;\nconst timeRegex =\n /^(\\d{2}(?:[.,]\\d*)?)(?::?(\\d{2}(?:[.,]\\d*)?))?(?::?(\\d{2}(?:[.,]\\d*)?))?$/;\nconst timezoneRegex = /^([+-])(\\d{2})(?::?(\\d{2}))?$/;\n\nfunction splitDateString(dateString) {\n const dateStrings = {};\n const array = dateString.split(patterns.dateTimeDelimiter);\n let timeString;\n\n // The regex match should only return at maximum two array elements.\n // [date], [time], or [date, time].\n if (array.length > 2) {\n return dateStrings;\n }\n\n if (/:/.test(array[0])) {\n timeString = array[0];\n } else {\n dateStrings.date = array[0];\n timeString = array[1];\n if (patterns.timeZoneDelimiter.test(dateStrings.date)) {\n dateStrings.date = dateString.split(patterns.timeZoneDelimiter)[0];\n timeString = dateString.substr(\n dateStrings.date.length,\n dateString.length,\n );\n }\n }\n\n if (timeString) {\n const token = patterns.timezone.exec(timeString);\n if (token) {\n dateStrings.time = timeString.replace(token[1], \"\");\n dateStrings.timezone = token[1];\n } else {\n dateStrings.time = timeString;\n }\n }\n\n return dateStrings;\n}\n\nfunction parseYear(dateString, additionalDigits) {\n const regex = new RegExp(\n \"^(?:(\\\\d{4}|[+-]\\\\d{\" +\n (4 + additionalDigits) +\n \"})|(\\\\d{2}|[+-]\\\\d{\" +\n (2 + additionalDigits) +\n \"})$)\",\n );\n\n const captures = dateString.match(regex);\n // Invalid ISO-formatted year\n if (!captures) return { year: NaN, restDateString: \"\" };\n\n const year = captures[1] ? parseInt(captures[1]) : null;\n const century = captures[2] ? parseInt(captures[2]) : null;\n\n // either year or century is null, not both\n return {\n year: century === null ? year : century * 100,\n restDateString: dateString.slice((captures[1] || captures[2]).length),\n };\n}\n\nfunction parseDate(dateString, year) {\n // Invalid ISO-formatted year\n if (year === null) return new Date(NaN);\n\n const captures = dateString.match(dateRegex);\n // Invalid ISO-formatted string\n if (!captures) return new Date(NaN);\n\n const isWeekDate = !!captures[4];\n const dayOfYear = parseDateUnit(captures[1]);\n const month = parseDateUnit(captures[2]) - 1;\n const day = parseDateUnit(captures[3]);\n const week = parseDateUnit(captures[4]);\n const dayOfWeek = parseDateUnit(captures[5]) - 1;\n\n if (isWeekDate) {\n if (!validateWeekDate(year, week, dayOfWeek)) {\n return new Date(NaN);\n }\n return dayOfISOWeekYear(year, week, dayOfWeek);\n } else {\n const date = new Date(0);\n if (\n !validateDate(year, month, day) ||\n !validateDayOfYearDate(year, dayOfYear)\n ) {\n return new Date(NaN);\n }\n date.setUTCFullYear(year, month, Math.max(dayOfYear, day));\n return date;\n }\n}\n\nfunction parseDateUnit(value) {\n return value ? parseInt(value) : 1;\n}\n\nfunction parseTime(timeString) {\n const captures = timeString.match(timeRegex);\n if (!captures) return NaN; // Invalid ISO-formatted time\n\n const hours = parseTimeUnit(captures[1]);\n const minutes = parseTimeUnit(captures[2]);\n const seconds = parseTimeUnit(captures[3]);\n\n if (!validateTime(hours, minutes, seconds)) {\n return NaN;\n }\n\n return (\n hours * millisecondsInHour + minutes * millisecondsInMinute + seconds * 1000\n );\n}\n\nfunction parseTimeUnit(value) {\n return (value && parseFloat(value.replace(\",\", \".\"))) || 0;\n}\n\nfunction parseTimezone(timezoneString) {\n if (timezoneString === \"Z\") return 0;\n\n const captures = timezoneString.match(timezoneRegex);\n if (!captures) return 0;\n\n const sign = captures[1] === \"+\" ? -1 : 1;\n const hours = parseInt(captures[2]);\n const minutes = (captures[3] && parseInt(captures[3])) || 0;\n\n if (!validateTimezone(hours, minutes)) {\n return NaN;\n }\n\n return sign * (hours * millisecondsInHour + minutes * millisecondsInMinute);\n}\n\nfunction dayOfISOWeekYear(isoWeekYear, week, day) {\n const date = new Date(0);\n date.setUTCFullYear(isoWeekYear, 0, 4);\n const fourthOfJanuaryDay = date.getUTCDay() || 7;\n const diff = (week - 1) * 7 + day + 1 - fourthOfJanuaryDay;\n date.setUTCDate(date.getUTCDate() + diff);\n return date;\n}\n\n// Validation functions\n\n// February is null to handle the leap year (using ||)\nconst daysInMonths = [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n\nfunction isLeapYearIndex(year) {\n return year % 400 === 0 || (year % 4 === 0 && year % 100 !== 0);\n}\n\nfunction validateDate(year, month, date) {\n return (\n month >= 0 &&\n month <= 11 &&\n date >= 1 &&\n date <= (daysInMonths[month] || (isLeapYearIndex(year) ? 29 : 28))\n );\n}\n\nfunction validateDayOfYearDate(year, dayOfYear) {\n return dayOfYear >= 1 && dayOfYear <= (isLeapYearIndex(year) ? 366 : 365);\n}\n\nfunction validateWeekDate(_year, week, day) {\n return week >= 1 && week <= 53 && day >= 0 && day <= 6;\n}\n\nfunction validateTime(hours, minutes, seconds) {\n if (hours === 24) {\n return minutes === 0 && seconds === 0;\n }\n\n return (\n seconds >= 0 &&\n seconds < 60 &&\n minutes >= 0 &&\n minutes < 60 &&\n hours >= 0 &&\n hours < 25\n );\n}\n\nfunction validateTimezone(_hours, minutes) {\n return minutes >= 0 && minutes <= 59;\n}\n\n// Fallback for modularized imports:\nexport default parseISO;\n","import clsx from 'clsx';\nimport { differenceInMilliseconds, formatISO, isValid, parseISO } from 'date-fns';\nimport { type ChangeEvent, useCallback, useEffect, useId, useRef, useState } from 'react';\nimport { Icon, type IconicNames, type IconicTypes } from '@clubmed/trident-icons';\n\nimport { useValue } from '../hooks/useValue';\nimport { useInternalStatus } from '../hooks/useInternalStatus';\nimport { FormControl, type FormControlProps } from './FormControl';\n\ninterface DateFieldProps extends Omit<FormControlProps<Date | null>, 'max' | 'min'> {\n description?: string;\n /**\n * Open datepicker text\n */\n openDatepicker?: string;\n id?: string;\n icon?: IconicNames;\n iconType?: IconicTypes;\n errorMessage?: string;\n dataTestId?: string;\n /**\n * Minimum Date allowed\n */\n min?: Date | undefined;\n /**\n * Maximum Date allowed\n */\n max?: Date | undefined;\n}\n\nconst formatDate = (value: Date | number | string | undefined | null): string => {\n if (!value) {\n return '';\n }\n\n // Already an ISO date string (YYYY-MM-DD) coming back through the useValue\n // formatter chain — return it as-is, otherwise `new Date(isoString)` would\n // parse it as UTC midnight and `formatISO(..., 'date')` (local time) would\n // shift the calendar day in negative-UTC timezones.\n if (typeof value === 'string') {\n return value;\n }\n\n if (!isValid(new Date(value))) {\n return '';\n }\n\n return formatISO(new Date(value), { representation: 'date' });\n};\n\nconst validateDateRange = (date: string, min?: Date, max?: Date): boolean => {\n if (!date) {\n return true;\n }\n\n const normalizeDate = (d: Date) => new Date(d.getFullYear(), d.getMonth(), d.getDate());\n // parseISO interprets a bare YYYY-MM-DD as local midnight, while `new Date(date)`\n // would interpret it as UTC midnight and silently shift the calendar day in\n // negative-UTC timezones.\n const inputDate = parseISO(date);\n const normalizedInputDate = normalizeDate(inputDate);\n\n if (min && normalizedInputDate < normalizeDate(min)) {\n return false;\n }\n if (max && normalizedInputDate > normalizeDate(max)) {\n return false;\n }\n\n return true;\n};\n\nexport const DateField = (props: DateFieldProps) => {\n const internalId = useId();\n\n const {\n id = internalId,\n name = id,\n label,\n description,\n openDatepicker,\n validationStatus = 'default',\n icon = 'CalendarDefault',\n iconType,\n errorMessage,\n disabled = false,\n required = false,\n hideRequiredStar,\n dataTestId = 'DateField',\n value: initialValue,\n min: initialMin,\n max: initialMax,\n onChange,\n className,\n ...rest\n } = props;\n\n // NO LOGIC IS IMPLEMENTED YET HERE\n // THIS IS A VISUAL COMPONENT ONLY\n const ref = useRef<HTMLInputElement>(null);\n const [isFocused, setIsFocused] = useState<boolean>(false);\n\n const internalStatus = useInternalStatus({\n isDisabled: disabled,\n validationStatus,\n });\n\n const { setValue, value } = useValue<string, Date | number | undefined | null>({\n formatter: formatDate as never,\n initialValue,\n onChange: (event, fieldValue) => onChange?.(event, fieldValue ? parseISO(fieldValue) : null),\n });\n\n const min = formatDate(initialMin);\n const max = formatDate(initialMax);\n\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n setValue(event.target.value, event.nativeEvent);\n };\n\n const handleClickPicker = () => {\n ref.current?.showPicker();\n };\n\n const handleOutOfRangeDate = useCallback(() => {\n const currentDate = parseISO(value);\n\n if (initialMin && initialMax) {\n const distanceToMin = Math.abs(differenceInMilliseconds(currentDate, initialMin));\n const distanceToMax = Math.abs(differenceInMilliseconds(currentDate, initialMax));\n setValue(distanceToMin <= distanceToMax ? min : max);\n return;\n }\n\n if (initialMin) {\n setValue(min);\n return;\n }\n\n if (initialMax) {\n setValue(max);\n return;\n }\n }, [initialMax, initialMin, max, min, setValue, value]);\n\n useEffect(() => {\n const isValid = validateDateRange(value, initialMin, initialMax);\n if (!isValid && !isFocused) {\n handleOutOfRangeDate();\n }\n }, [handleOutOfRangeDate, initialMax, initialMin, isFocused, value]);\n\n return (\n <FormControl\n id={id}\n label={label}\n description={description}\n dataName=\"DateField\"\n dataTestId={dataTestId}\n disabled={disabled}\n required={required}\n hideRequiredStar={hideRequiredStar}\n validationStatus={validationStatus}\n errorMessage={errorMessage}\n className={className}\n >\n <div className=\"relative\" role=\"group\">\n <input\n {...rest}\n type=\"date\"\n className={clsx(\n 'text-b3 rounded-pill date-field min-h-[50px] w-full border px-20 py-12 ps-[52px] font-normal outline-none',\n {\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'pe-[52px]': internalStatus === 'error' || internalStatus === 'success',\n 'bg-pearl border-middleGrey': disabled,\n 'bg-white text-black': !disabled,\n 'border-red': !disabled && internalStatus === 'error',\n 'border-green': !disabled && internalStatus === 'success',\n },\n )}\n id={id}\n name={name}\n disabled={disabled}\n required={required}\n ref={ref}\n value={value}\n min={min}\n max={max}\n onChange={handleChange}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n />\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12',\n {\n 'text-grey': disabled,\n 'text-red': !disabled && internalStatus === 'error',\n 'text-green': !disabled && internalStatus === 'success',\n },\n )}\n >\n <button className=\"pointer-events-auto leading-none\" onClick={handleClickPicker}>\n <Icon name={icon} iconType={iconType} width=\"24px\" />\n <span className=\"sr-only\">\n {openDatepicker}\n {/* REPLACE THAT WITH SOMETHING LIKE \"OPEN DATEPICKER\" SOONER RATHER THAN LATER PLZ */}\n </span>\n </button>\n <span className=\"ms-auto flex gap-x-8\">\n {internalStatus === 'error' && <Icon name=\"CrossDefault\" width=\"24px\" />}\n {internalStatus === 'success' && <Icon name=\"CheckDefault\" width=\"24px\" />}\n </span>\n </div>\n </div>\n </FormControl>\n );\n};\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DateField.js","names":[],"sources":["../../../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/differenceInMilliseconds.js","../../../../node_modules/.pnpm/date-fns@4.1.0/node_modules/date-fns/parseISO.js","../../lib/ui/forms/DateField.tsx"],"sourcesContent":["import { toDate } from \"./toDate.js\";\n\n/**\n * @name differenceInMilliseconds\n * @category Millisecond Helpers\n * @summary Get the number of milliseconds between the given dates.\n *\n * @description\n * Get the number of milliseconds between the given dates.\n *\n * @param laterDate - The later date\n * @param earlierDate - The earlier date\n *\n * @returns The number of milliseconds\n *\n * @example\n * // How many milliseconds are between\n * // 2 July 2014 12:30:20.600 and 2 July 2014 12:30:21.700?\n * const result = differenceInMilliseconds(\n * new Date(2014, 6, 2, 12, 30, 21, 700),\n * new Date(2014, 6, 2, 12, 30, 20, 600)\n * )\n * //=> 1100\n */\nexport function differenceInMilliseconds(laterDate, earlierDate) {\n return +toDate(laterDate) - +toDate(earlierDate);\n}\n\n// Fallback for modularized imports:\nexport default differenceInMilliseconds;\n","import {\n millisecondsInHour,\n millisecondsInMinute,\n} from \"./constants.js\";\nimport { constructFrom } from \"./constructFrom.js\";\nimport { toDate } from \"./toDate.js\";\n\n/**\n * The {@link parseISO} function options.\n */\n\n/**\n * @name parseISO\n * @category Common Helpers\n * @summary Parse ISO string\n *\n * @description\n * Parse the given string in ISO 8601 format and return an instance of Date.\n *\n * Function accepts complete ISO 8601 formats as well as partial implementations.\n * ISO 8601: http://en.wikipedia.org/wiki/ISO_8601\n *\n * If the argument isn't a string, the function cannot parse the string or\n * the values are invalid, it returns Invalid Date.\n *\n * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).\n * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments.\n *\n * @param argument - The value to convert\n * @param options - An object with options\n *\n * @returns The parsed date in the local time zone\n *\n * @example\n * // Convert string '2014-02-11T11:30:30' to date:\n * const result = parseISO('2014-02-11T11:30:30')\n * //=> Tue Feb 11 2014 11:30:30\n *\n * @example\n * // Convert string '+02014101' to date,\n * // if the additional number of digits in the extended year format is 1:\n * const result = parseISO('+02014101', { additionalDigits: 1 })\n * //=> Fri Apr 11 2014 00:00:00\n */\nexport function parseISO(argument, options) {\n const invalidDate = () => constructFrom(options?.in, NaN);\n\n const additionalDigits = options?.additionalDigits ?? 2;\n const dateStrings = splitDateString(argument);\n\n let date;\n if (dateStrings.date) {\n const parseYearResult = parseYear(dateStrings.date, additionalDigits);\n date = parseDate(parseYearResult.restDateString, parseYearResult.year);\n }\n\n if (!date || isNaN(+date)) return invalidDate();\n\n const timestamp = +date;\n let time = 0;\n let offset;\n\n if (dateStrings.time) {\n time = parseTime(dateStrings.time);\n if (isNaN(time)) return invalidDate();\n }\n\n if (dateStrings.timezone) {\n offset = parseTimezone(dateStrings.timezone);\n if (isNaN(offset)) return invalidDate();\n } else {\n const tmpDate = new Date(timestamp + time);\n const result = toDate(0, options?.in);\n result.setFullYear(\n tmpDate.getUTCFullYear(),\n tmpDate.getUTCMonth(),\n tmpDate.getUTCDate(),\n );\n result.setHours(\n tmpDate.getUTCHours(),\n tmpDate.getUTCMinutes(),\n tmpDate.getUTCSeconds(),\n tmpDate.getUTCMilliseconds(),\n );\n return result;\n }\n\n return toDate(timestamp + time + offset, options?.in);\n}\n\nconst patterns = {\n dateTimeDelimiter: /[T ]/,\n timeZoneDelimiter: /[Z ]/i,\n timezone: /([Z+-].*)$/,\n};\n\nconst dateRegex =\n /^-?(?:(\\d{3})|(\\d{2})(?:-?(\\d{2}))?|W(\\d{2})(?:-?(\\d{1}))?|)$/;\nconst timeRegex =\n /^(\\d{2}(?:[.,]\\d*)?)(?::?(\\d{2}(?:[.,]\\d*)?))?(?::?(\\d{2}(?:[.,]\\d*)?))?$/;\nconst timezoneRegex = /^([+-])(\\d{2})(?::?(\\d{2}))?$/;\n\nfunction splitDateString(dateString) {\n const dateStrings = {};\n const array = dateString.split(patterns.dateTimeDelimiter);\n let timeString;\n\n // The regex match should only return at maximum two array elements.\n // [date], [time], or [date, time].\n if (array.length > 2) {\n return dateStrings;\n }\n\n if (/:/.test(array[0])) {\n timeString = array[0];\n } else {\n dateStrings.date = array[0];\n timeString = array[1];\n if (patterns.timeZoneDelimiter.test(dateStrings.date)) {\n dateStrings.date = dateString.split(patterns.timeZoneDelimiter)[0];\n timeString = dateString.substr(\n dateStrings.date.length,\n dateString.length,\n );\n }\n }\n\n if (timeString) {\n const token = patterns.timezone.exec(timeString);\n if (token) {\n dateStrings.time = timeString.replace(token[1], \"\");\n dateStrings.timezone = token[1];\n } else {\n dateStrings.time = timeString;\n }\n }\n\n return dateStrings;\n}\n\nfunction parseYear(dateString, additionalDigits) {\n const regex = new RegExp(\n \"^(?:(\\\\d{4}|[+-]\\\\d{\" +\n (4 + additionalDigits) +\n \"})|(\\\\d{2}|[+-]\\\\d{\" +\n (2 + additionalDigits) +\n \"})$)\",\n );\n\n const captures = dateString.match(regex);\n // Invalid ISO-formatted year\n if (!captures) return { year: NaN, restDateString: \"\" };\n\n const year = captures[1] ? parseInt(captures[1]) : null;\n const century = captures[2] ? parseInt(captures[2]) : null;\n\n // either year or century is null, not both\n return {\n year: century === null ? year : century * 100,\n restDateString: dateString.slice((captures[1] || captures[2]).length),\n };\n}\n\nfunction parseDate(dateString, year) {\n // Invalid ISO-formatted year\n if (year === null) return new Date(NaN);\n\n const captures = dateString.match(dateRegex);\n // Invalid ISO-formatted string\n if (!captures) return new Date(NaN);\n\n const isWeekDate = !!captures[4];\n const dayOfYear = parseDateUnit(captures[1]);\n const month = parseDateUnit(captures[2]) - 1;\n const day = parseDateUnit(captures[3]);\n const week = parseDateUnit(captures[4]);\n const dayOfWeek = parseDateUnit(captures[5]) - 1;\n\n if (isWeekDate) {\n if (!validateWeekDate(year, week, dayOfWeek)) {\n return new Date(NaN);\n }\n return dayOfISOWeekYear(year, week, dayOfWeek);\n } else {\n const date = new Date(0);\n if (\n !validateDate(year, month, day) ||\n !validateDayOfYearDate(year, dayOfYear)\n ) {\n return new Date(NaN);\n }\n date.setUTCFullYear(year, month, Math.max(dayOfYear, day));\n return date;\n }\n}\n\nfunction parseDateUnit(value) {\n return value ? parseInt(value) : 1;\n}\n\nfunction parseTime(timeString) {\n const captures = timeString.match(timeRegex);\n if (!captures) return NaN; // Invalid ISO-formatted time\n\n const hours = parseTimeUnit(captures[1]);\n const minutes = parseTimeUnit(captures[2]);\n const seconds = parseTimeUnit(captures[3]);\n\n if (!validateTime(hours, minutes, seconds)) {\n return NaN;\n }\n\n return (\n hours * millisecondsInHour + minutes * millisecondsInMinute + seconds * 1000\n );\n}\n\nfunction parseTimeUnit(value) {\n return (value && parseFloat(value.replace(\",\", \".\"))) || 0;\n}\n\nfunction parseTimezone(timezoneString) {\n if (timezoneString === \"Z\") return 0;\n\n const captures = timezoneString.match(timezoneRegex);\n if (!captures) return 0;\n\n const sign = captures[1] === \"+\" ? -1 : 1;\n const hours = parseInt(captures[2]);\n const minutes = (captures[3] && parseInt(captures[3])) || 0;\n\n if (!validateTimezone(hours, minutes)) {\n return NaN;\n }\n\n return sign * (hours * millisecondsInHour + minutes * millisecondsInMinute);\n}\n\nfunction dayOfISOWeekYear(isoWeekYear, week, day) {\n const date = new Date(0);\n date.setUTCFullYear(isoWeekYear, 0, 4);\n const fourthOfJanuaryDay = date.getUTCDay() || 7;\n const diff = (week - 1) * 7 + day + 1 - fourthOfJanuaryDay;\n date.setUTCDate(date.getUTCDate() + diff);\n return date;\n}\n\n// Validation functions\n\n// February is null to handle the leap year (using ||)\nconst daysInMonths = [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n\nfunction isLeapYearIndex(year) {\n return year % 400 === 0 || (year % 4 === 0 && year % 100 !== 0);\n}\n\nfunction validateDate(year, month, date) {\n return (\n month >= 0 &&\n month <= 11 &&\n date >= 1 &&\n date <= (daysInMonths[month] || (isLeapYearIndex(year) ? 29 : 28))\n );\n}\n\nfunction validateDayOfYearDate(year, dayOfYear) {\n return dayOfYear >= 1 && dayOfYear <= (isLeapYearIndex(year) ? 366 : 365);\n}\n\nfunction validateWeekDate(_year, week, day) {\n return week >= 1 && week <= 53 && day >= 0 && day <= 6;\n}\n\nfunction validateTime(hours, minutes, seconds) {\n if (hours === 24) {\n return minutes === 0 && seconds === 0;\n }\n\n return (\n seconds >= 0 &&\n seconds < 60 &&\n minutes >= 0 &&\n minutes < 60 &&\n hours >= 0 &&\n hours < 25\n );\n}\n\nfunction validateTimezone(_hours, minutes) {\n return minutes >= 0 && minutes <= 59;\n}\n\n// Fallback for modularized imports:\nexport default parseISO;\n","import clsx from 'clsx';\nimport { differenceInMilliseconds, formatISO, isValid, parseISO } from 'date-fns';\nimport { type ChangeEvent, useCallback, useEffect, useId, useRef, useState } from 'react';\nimport { Icon, type IconicNames, type IconicTypes } from '@clubmed/trident-icons';\n\nimport { useValue } from '../hooks/useValue';\nimport { useInternalStatus } from '../hooks/useInternalStatus';\nimport { FormControl, type FormControlProps } from './FormControl';\n\ninterface DateFieldProps extends Omit<FormControlProps<Date | null>, 'max' | 'min'> {\n description?: string;\n /**\n * Open datepicker text\n */\n openDatepicker?: string;\n id?: string;\n icon?: IconicNames;\n iconType?: IconicTypes;\n errorMessage?: string;\n dataTestId?: string;\n /**\n * Minimum Date allowed\n */\n min?: Date | undefined;\n /**\n * Maximum Date allowed\n */\n max?: Date | undefined;\n}\n\nconst formatDate = (value: Date | number | string | undefined | null): string => {\n if (!value) {\n return '';\n }\n\n // Already an ISO date string (YYYY-MM-DD) coming back through the useValue\n // formatter chain — return it as-is, otherwise `new Date(isoString)` would\n // parse it as UTC midnight and `formatISO(..., 'date')` (local time) would\n // shift the calendar day in negative-UTC timezones.\n if (typeof value === 'string') {\n return value;\n }\n\n if (!isValid(new Date(value))) {\n return '';\n }\n\n return formatISO(new Date(value), { representation: 'date' });\n};\n\nconst validateDateRange = (date: string, min?: Date, max?: Date): boolean => {\n if (!date) {\n return true;\n }\n\n const normalizeDate = (d: Date) => new Date(d.getFullYear(), d.getMonth(), d.getDate());\n // parseISO interprets a bare YYYY-MM-DD as local midnight, while `new Date(date)`\n // would interpret it as UTC midnight and silently shift the calendar day in\n // negative-UTC timezones.\n const inputDate = parseISO(date);\n const normalizedInputDate = normalizeDate(inputDate);\n\n if (min && normalizedInputDate < normalizeDate(min)) {\n return false;\n }\n if (max && normalizedInputDate > normalizeDate(max)) {\n return false;\n }\n\n return true;\n};\n\nexport const DateField = (props: DateFieldProps) => {\n const internalId = useId();\n\n const {\n id = internalId,\n name = id,\n label,\n description,\n openDatepicker,\n validationStatus = 'default',\n icon = 'CalendarDefault',\n iconType,\n errorMessage,\n disabled = false,\n required = false,\n hideRequiredStar,\n dataTestId = 'DateField',\n value: initialValue,\n min: initialMin,\n max: initialMax,\n onChange,\n className,\n ...rest\n } = props;\n\n // NO LOGIC IS IMPLEMENTED YET HERE\n // THIS IS A VISUAL COMPONENT ONLY\n const ref = useRef<HTMLInputElement>(null);\n const [isFocused, setIsFocused] = useState<boolean>(false);\n\n const internalStatus = useInternalStatus({\n isDisabled: disabled,\n validationStatus,\n });\n\n const { setValue, value } = useValue<string, Date | number | undefined | null>({\n formatter: formatDate as never,\n initialValue,\n onChange: (event, fieldValue) => onChange?.(event, fieldValue ? parseISO(fieldValue) : null),\n });\n\n const min = formatDate(initialMin);\n const max = formatDate(initialMax);\n\n const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n setValue(event.target.value, event.nativeEvent);\n };\n\n const handleClickPicker = () => {\n ref.current?.showPicker();\n };\n\n const handleOutOfRangeDate = useCallback(() => {\n const currentDate = parseISO(value);\n\n if (initialMin && initialMax) {\n const distanceToMin = Math.abs(differenceInMilliseconds(currentDate, initialMin));\n const distanceToMax = Math.abs(differenceInMilliseconds(currentDate, initialMax));\n setValue(distanceToMin <= distanceToMax ? min : max);\n return;\n }\n\n if (initialMin) {\n setValue(min);\n return;\n }\n\n if (initialMax) {\n setValue(max);\n return;\n }\n }, [initialMax, initialMin, max, min, setValue, value]);\n\n useEffect(() => {\n const isValid = validateDateRange(value, initialMin, initialMax);\n if (!isValid && !isFocused) {\n handleOutOfRangeDate();\n }\n }, [handleOutOfRangeDate, initialMax, initialMin, isFocused, value]);\n\n return (\n <FormControl\n id={id}\n label={label}\n description={description}\n dataName=\"DateField\"\n dataTestId={dataTestId}\n disabled={disabled}\n required={required}\n hideRequiredStar={hideRequiredStar}\n validationStatus={validationStatus}\n errorMessage={errorMessage}\n className={className}\n >\n <div className=\"relative\" role=\"group\">\n <input\n {...rest}\n type=\"date\"\n className={clsx(\n 'text-b3 rounded-pill date-field min-h-[50px] w-full border px-20 py-12 ps-[52px] font-normal outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2',\n {\n 'border-middleGrey focus:border-black active:border-black':\n internalStatus === 'default',\n 'pe-[52px]': internalStatus === 'error' || internalStatus === 'success',\n 'bg-pearl border-middleGrey': disabled,\n 'bg-white text-black': !disabled,\n 'border-red': !disabled && internalStatus === 'error',\n 'border-green': !disabled && internalStatus === 'success',\n },\n )}\n id={id}\n name={name}\n disabled={disabled}\n required={required}\n ref={ref}\n value={value}\n min={min}\n max={max}\n onChange={handleChange}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n />\n <div\n className={clsx(\n 'pointer-events-none absolute inset-0 flex items-center justify-between px-20 py-12',\n {\n 'text-grey': disabled,\n 'text-red': !disabled && internalStatus === 'error',\n 'text-green': !disabled && internalStatus === 'success',\n },\n )}\n >\n <button\n className=\"pointer-events-auto leading-none outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2\"\n onClick={handleClickPicker}\n >\n <Icon name={icon} iconType={iconType} width=\"24px\" />\n <span className=\"sr-only\">\n {openDatepicker}\n {/* REPLACE THAT WITH SOMETHING LIKE \"OPEN DATEPICKER\" SOONER RATHER THAN LATER PLZ */}\n </span>\n </button>\n <span className=\"ms-auto flex gap-x-8\">\n {internalStatus === 'error' && <Icon name=\"CrossDefault\" width=\"24px\" />}\n {internalStatus === 'success' && <Icon name=\"CheckDefault\" width=\"24px\" />}\n </span>\n </div>\n </div>\n </FormControl>\n );\n};\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
|
package/package.json
CHANGED
package/styles/ui/controls.css
CHANGED
|
@@ -33,8 +33,7 @@
|
|
|
33
33
|
@apply border-black;
|
|
34
34
|
}
|
|
35
35
|
label > span > input[type='radio']:focus-visible + span {
|
|
36
|
-
@apply ring-
|
|
37
|
-
--tw-ring-color: color-mix(in oklab, var(--color-lavender) 20%, transparent);
|
|
36
|
+
@apply ring-4 ring-black ring-offset-2;
|
|
38
37
|
}
|
|
39
38
|
label > span > input[type='radio'] + span {
|
|
40
39
|
@apply rounded-full;
|
|
@@ -86,8 +85,7 @@
|
|
|
86
85
|
transform: scale(1);
|
|
87
86
|
}
|
|
88
87
|
label > input[type='checkbox'][data-name='Filter']:focus-visible + span {
|
|
89
|
-
@apply ring-
|
|
90
|
-
--tw-ring-color: color-mix(in oklab, var(--color-lavender) 20%, transparent);
|
|
88
|
+
@apply ring-4 ring-black ring-offset-2;
|
|
91
89
|
}
|
|
92
90
|
/* Switch */
|
|
93
91
|
input[role='switch'][data-name='Switch'] + span {
|
|
@@ -123,7 +121,6 @@
|
|
|
123
121
|
opacity: 1;
|
|
124
122
|
}
|
|
125
123
|
input[role='switch'][data-name='Switch']:focus-visible + span {
|
|
126
|
-
@apply ring-
|
|
127
|
-
--tw-ring-color: color-mix(in oklab, var(--color-lavender) 20%, transparent);
|
|
124
|
+
@apply ring-4 ring-black ring-offset-2;
|
|
128
125
|
}
|
|
129
126
|
}
|
package/styles/ui/range.css
CHANGED
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
@apply m-0;
|
|
55
55
|
@apply pointer-events-none;
|
|
56
56
|
@apply appearance-none;
|
|
57
|
+
@apply outline-none;
|
|
57
58
|
|
|
58
59
|
grid-column: 1 / span 2;
|
|
59
60
|
grid-row: 3;
|
|
@@ -61,6 +62,10 @@
|
|
|
61
62
|
--col: #000;
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
.input-range:focus-visible {
|
|
66
|
+
@apply ring-4 ring-black ring-offset-2;
|
|
67
|
+
}
|
|
68
|
+
|
|
64
69
|
.input-range-sliderThumb {
|
|
65
70
|
@apply transform;
|
|
66
71
|
@apply appearance-none;
|
package/ui/AdvancedToast.js
CHANGED
|
@@ -49,7 +49,7 @@ var h = ({ className: h, title: g, body: _, icon: v, isVisible: y, labels: b, ch
|
|
|
49
49
|
}), /* @__PURE__ */ p("p", { children: _ })] }), x && /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ m("button", {
|
|
50
50
|
onClick: () => A(!k),
|
|
51
51
|
"aria-expanded": k,
|
|
52
|
-
className: "flex w-fit items-center gap-8 text-b5 font-normal",
|
|
52
|
+
className: "flex w-fit items-center gap-8 text-b5 font-normal outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2",
|
|
53
53
|
type: "button",
|
|
54
54
|
children: [/* @__PURE__ */ p("span", { children: b.details }), /* @__PURE__ */ p(f, {
|
|
55
55
|
name: "ArrowDefaultDown",
|
|
@@ -68,7 +68,7 @@ var h = ({ className: h, title: g, body: _, icon: v, isVisible: y, labels: b, ch
|
|
|
68
68
|
w && /* @__PURE__ */ p("button", {
|
|
69
69
|
"aria-describedby": O,
|
|
70
70
|
"aria-label": b.close,
|
|
71
|
-
className: "absolute end-0 top-0 mt-20 me-13 text-black",
|
|
71
|
+
className: "absolute end-0 top-0 mt-20 me-13 text-black outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2",
|
|
72
72
|
onClick: w,
|
|
73
73
|
type: "button",
|
|
74
74
|
children: /* @__PURE__ */ p(f, {
|
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-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 <span className={clsx('w-30 h-30 pt-4', getTextColor(accentColor))}>\n <Icon name={icon} width=\"30px\" className={clsx('flex-shrink-0')} />\n </span>\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,QAAD;IAAM,WAAW,EAAK,kBAAkB,EAAa,EAAY,CAAC;cAChE,kBAAC,GAAD;KAAM,MAAM;KAAM,OAAM;KAAO,WAAW,EAAK,gBAAA;KAAoB,CAAA;IAC9D,CAAA;GACP,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"}
|
|
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 <span className={clsx('w-30 h-30 pt-4', getTextColor(accentColor))}>\n <Icon name={icon} width=\"30px\" className={clsx('flex-shrink-0')} />\n </span>\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 outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2\"\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 outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2\"\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,QAAD;IAAM,WAAW,EAAK,kBAAkB,EAAa,EAAY,CAAC;cAChE,kBAAC,GAAD;KAAM,MAAM;KAAM,OAAM;KAAO,WAAW,EAAK,gBAAA;KAAoB,CAAA;IAC9D,CAAA;GACP,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/Avatar.js
CHANGED
|
@@ -9,7 +9,7 @@ function i({ alt: i, src: a, srcSet: o, style: s, children: c, className: l, ...
|
|
|
9
9
|
role: "figure",
|
|
10
10
|
"aria-label": i
|
|
11
11
|
} : {},
|
|
12
|
-
className: e("flex items-center justify-center relative overflow-hidden", {
|
|
12
|
+
className: e("flex items-center justify-center relative overflow-hidden outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2", {
|
|
13
13
|
"rounded-full": !l?.includes("rounded-"),
|
|
14
14
|
"text-black": !l?.includes("text-"),
|
|
15
15
|
"bg-saffron": !l?.includes("bg-")
|
package/ui/Avatar.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Avatar.js","names":[],"sources":["../../lib/ui/Avatar.tsx"],"sourcesContent":["import type { HTMLAttributes, PropsWithChildren } from 'react';\nimport clsx from 'clsx';\n\nconst initial = (text = '') => {\n return text\n .split(' ')\n .map((text) => text.slice(0, 1))\n .join('');\n};\n\nexport interface AvatarProps extends HTMLAttributes<HTMLDivElement> {\n alt?: string;\n src?: string | undefined;\n srcSet?: string | undefined;\n}\n\nexport function Avatar({\n alt,\n src,\n srcSet,\n style,\n children,\n className,\n ...props\n}: PropsWithChildren<AvatarProps>) {\n return (\n <div\n {...props}\n {...(src || srcSet ? { role: 'figure', 'aria-label': alt } : {})}\n className={clsx(\n 'flex items-center justify-center relative overflow-hidden',\n {\n 'rounded-full': !className?.includes('rounded-'),\n 'text-black': !className?.includes('text-'),\n 'bg-saffron': !className?.includes('bg-'),\n },\n className,\n )}\n style={{\n width: '48px',\n height: '48px',\n ...style,\n }}\n >\n <span className=\"flex items-center justify-center font-bold uppercase text-h6 h-full\">\n {src || srcSet ? (\n <img\n srcSet={srcSet}\n src={src}\n alt={alt}\n className={'w-full h-full object-cover transparent indent-[1000px]'}\n />\n ) : (\n initial(alt)\n )}\n {children}\n </span>\n </div>\n );\n}\n"],"mappings":";;;AAGA,IAAM,KAAW,IAAO,OACf,EACJ,MAAM,IAAI,CACV,KAAK,MAAS,EAAK,MAAM,GAAG,EAAE,CAAC,CAC/B,KAAK,GAAG;AASb,SAAgB,EAAO,EACrB,QACA,QACA,WACA,UACA,aACA,cACA,GAAG,KAC8B;AACjC,QACE,kBAAC,OAAD;EACE,GAAI;EACJ,GAAK,KAAO,IAAS;GAAE,MAAM;GAAU,cAAc;GAAK,GAAG,EAAE;EAC/D,WAAW,EACT,
|
|
1
|
+
{"version":3,"file":"Avatar.js","names":[],"sources":["../../lib/ui/Avatar.tsx"],"sourcesContent":["import type { HTMLAttributes, PropsWithChildren } from 'react';\nimport clsx from 'clsx';\n\nconst initial = (text = '') => {\n return text\n .split(' ')\n .map((text) => text.slice(0, 1))\n .join('');\n};\n\nexport interface AvatarProps extends HTMLAttributes<HTMLDivElement> {\n alt?: string;\n src?: string | undefined;\n srcSet?: string | undefined;\n}\n\nexport function Avatar({\n alt,\n src,\n srcSet,\n style,\n children,\n className,\n ...props\n}: PropsWithChildren<AvatarProps>) {\n return (\n <div\n {...props}\n {...(src || srcSet ? { role: 'figure', 'aria-label': alt } : {})}\n className={clsx(\n 'flex items-center justify-center relative overflow-hidden outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2',\n {\n 'rounded-full': !className?.includes('rounded-'),\n 'text-black': !className?.includes('text-'),\n 'bg-saffron': !className?.includes('bg-'),\n },\n className,\n )}\n style={{\n width: '48px',\n height: '48px',\n ...style,\n }}\n >\n <span className=\"flex items-center justify-center font-bold uppercase text-h6 h-full\">\n {src || srcSet ? (\n <img\n srcSet={srcSet}\n src={src}\n alt={alt}\n className={'w-full h-full object-cover transparent indent-[1000px]'}\n />\n ) : (\n initial(alt)\n )}\n {children}\n </span>\n </div>\n );\n}\n"],"mappings":";;;AAGA,IAAM,KAAW,IAAO,OACf,EACJ,MAAM,IAAI,CACV,KAAK,MAAS,EAAK,MAAM,GAAG,EAAE,CAAC,CAC/B,KAAK,GAAG;AASb,SAAgB,EAAO,EACrB,QACA,QACA,WACA,UACA,aACA,cACA,GAAG,KAC8B;AACjC,QACE,kBAAC,OAAD;EACE,GAAI;EACJ,GAAK,KAAO,IAAS;GAAE,MAAM;GAAU,cAAc;GAAK,GAAG,EAAE;EAC/D,WAAW,EACT,oJACA;GACE,gBAAgB,CAAC,GAAW,SAAS,WAAW;GAChD,cAAc,CAAC,GAAW,SAAS,QAAQ;GAC3C,cAAc,CAAC,GAAW,SAAS,MAAM;GAC1C,EACD,EACD;EACD,OAAO;GACL,OAAO;GACP,QAAQ;GACR,GAAG;GACJ;YAED,kBAAC,QAAD;GAAM,WAAU;aAAhB,CACG,KAAO,IACN,kBAAC,OAAD;IACU;IACH;IACA;IACL,WAAW;IACX,CAAA,GAEF,EAAQ,EAAI,EAEb,EACI;;EACH,CAAA"}
|
package/ui/BasicToast.js
CHANGED
|
@@ -18,7 +18,7 @@ function l({ className: l, description: u, href: d, icon: f, incentive: p, isVis
|
|
|
18
18
|
width: "30px"
|
|
19
19
|
}),
|
|
20
20
|
/* @__PURE__ */ c("a", {
|
|
21
|
-
className: "group pe-44",
|
|
21
|
+
className: "group pe-44 outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 rounded-pill",
|
|
22
22
|
href: d,
|
|
23
23
|
onClick: g,
|
|
24
24
|
children: [
|
|
@@ -41,7 +41,7 @@ function l({ className: l, description: u, href: d, icon: f, incentive: p, isVis
|
|
|
41
41
|
_ && /* @__PURE__ */ s("button", {
|
|
42
42
|
"aria-describedby": C,
|
|
43
43
|
"aria-label": h.close,
|
|
44
|
-
className: e("absolute end-0 top-0 m-20", n(S)),
|
|
44
|
+
className: e("absolute end-0 top-0 m-20 outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 rounded-pill", n(S)),
|
|
45
45
|
onClick: _,
|
|
46
46
|
children: /* @__PURE__ */ s(o, {
|
|
47
47
|
name: "CrossDefault",
|
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-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
|
|
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\n className=\"group pe-44 outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 rounded-pill\"\n href={href}\n onClick={onClick}\n >\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(\n 'absolute end-0 top-0 m-20 outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 rounded-pill',\n getComplementaryTextColor(color),\n )}\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;IACE,WAAU;IACJ;IACG;cAHX;KAKE,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,EACT,iIACA,EAA0B,EAAM,CACjC;IACD,SAAS;cAET,kBAAC,GAAD;KAAM,MAAK;KAAe,OAAM;KAAS,CAAA;IAClC,CAAA"}
|
package/ui/Breadcrumb.js
CHANGED
|
@@ -32,6 +32,7 @@ var o = ({ className: o, items: s, theme: c = "light", ...l }) => {
|
|
|
32
32
|
key: t
|
|
33
33
|
}, c ? /* @__PURE__ */ a("a", {
|
|
34
34
|
href: n,
|
|
35
|
+
className: "rounded-pill outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2",
|
|
35
36
|
children: t
|
|
36
37
|
}) : t, !s && /* @__PURE__ */ a(i, {
|
|
37
38
|
className: "mx-4",
|
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 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
|
|
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 ? (\n <a\n href={href}\n className=\"rounded-pill outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2\"\n >\n {label}\n </a>\n ) : (\n label\n )}\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;KAaF,EAXF,IACC,kBAAC,KAAD;KACQ;KACN,WAAU;eAET;KACC,CAAA,GAEJ,GAED,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-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-
|
|
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-4 focus-visible:ring-black focus-visible:ring-offset-2 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 = 'AI Agent: Have you got 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-
|
|
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 = 'AI Agent: Have you got 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-4 focus-visible:ring-black focus-visible:ring-offset-2 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,sCACtB,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,mWACA,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"}
|
package/ui/ChatInput.js
CHANGED
|
@@ -107,7 +107,7 @@ function m({ value: n, placeholder: r = "Ask a question?", onValueChange: u, onS
|
|
|
107
107
|
placeholder: r,
|
|
108
108
|
"aria-label": r,
|
|
109
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]")
|
|
110
|
+
className: t("scrollbar-hidden bg-transparent text-b3 py-12 outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 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
111
|
}), /* @__PURE__ */ o("div", {
|
|
112
112
|
style: {
|
|
113
113
|
insetInlineStart: b ? `calc(50% - ${E / 2}px)` : `calc(100% - ${s + E}px)`,
|
package/ui/ChatInput.js.map
CHANGED
|
@@ -1 +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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"}
|
|
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 focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 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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"}
|
package/ui/ChatWindow.js
CHANGED
|
@@ -67,7 +67,7 @@ var h = /* @__PURE__ */ p("svg", {
|
|
|
67
67
|
}), T ?? /* @__PURE__ */ p("button", {
|
|
68
68
|
type: "button",
|
|
69
69
|
"aria-label": "AI assistant",
|
|
70
|
-
className: "chat-border flex items-center justify-center size-48 rounded-full bg-white",
|
|
70
|
+
className: "chat-border flex items-center justify-center size-48 rounded-full bg-white outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2",
|
|
71
71
|
children: /* @__PURE__ */ p(f, {
|
|
72
72
|
name: "Sparkles",
|
|
73
73
|
width: "24px"
|
|
@@ -128,7 +128,7 @@ var h = /* @__PURE__ */ p("svg", {
|
|
|
128
128
|
type: "button",
|
|
129
129
|
"aria-label": "Close chat",
|
|
130
130
|
onClick: C,
|
|
131
|
-
className: "flex items-center justify-center size-48 rounded-full bg-black border border-black text-white shrink-0 hover:opacity-80 transition-opacity",
|
|
131
|
+
className: "flex items-center justify-center size-48 rounded-full bg-black border border-black text-white shrink-0 hover:opacity-80 transition-opacity outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2",
|
|
132
132
|
children: /* @__PURE__ */ p(f, {
|
|
133
133
|
name: "CrossDefault",
|
|
134
134
|
width: "24px"
|
package/ui/ChatWindow.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatWindow.js","names":[],"sources":["../../lib/ui/ChatWindow.tsx"],"sourcesContent":["'use client';\n\nimport { Icon } from '@clubmed/trident-icons';\nimport type { HTMLAttributes, ReactNode } from 'react';\nimport { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';\nimport { twMerge } from './helpers/twMerge';\nimport { ChatInput } from './ChatInput';\nimport { ChatMessage } from './ChatMessage';\nimport type { ChatMessageSender } from './ChatMessage';\nimport { ChatTypingIndicator } from './ChatTypingIndicator';\nimport { ChatWindowCsatPrompt } from './ChatWindowCsatPrompt';\nimport type { ChatWindowCsatPromptLabels } from './ChatWindowCsatPrompt';\nimport { ChatWindowCsatThankYou } from './ChatWindowCsatThankYou';\nimport type { ChatWindowCsatThankYouLabels } from './ChatWindowCsatThankYou';\nimport { ChatWindowQuote } from './ChatWindowQuote';\nimport type { ChatWindowQuoteItem } from './ChatWindowQuote';\n\nexport type { ChatWindowCsatPromptLabels, ChatWindowCsatThankYouLabels, ChatWindowQuoteItem };\n\nexport interface ChatWindowMessage {\n id: string;\n sender: ChatMessageSender;\n content: string;\n}\n\nexport interface ChatWindowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {\n title?: string;\n greeting?: string;\n messages: ChatWindowMessage[];\n inputValue: string;\n inputPlaceholder?: string;\n isTyping?: boolean;\n onInputChange: (value: string) => void;\n onSubmit: () => void;\n onClose?: () => void;\n onAudioClick?: () => void;\n logo?: ReactNode;\n onFeedbackRate?: (rating: number) => void;\n csatLabels?: ChatWindowCsatPromptLabels;\n csatRenderRatingLabel?: (rating: number) => ReactNode;\n csatThankYouLabels?: ChatWindowCsatThankYouLabels;\n onQuoteCtaClick?: (quote: ChatWindowQuoteItem) => void;\n}\n\nexport interface ChatWindowHandle {\n askForFeedback: () => void;\n showQuotes: (quotes: ChatWindowQuoteItem[]) => void;\n}\n\nconst headerCurve = (\n <svg\n className=\"absolute top-full inset-x-[50%] -translate-x-1/2\"\n width=\"118\"\n height=\"27\"\n viewBox=\"0 0 118 27\"\n >\n <path\n d=\"M0 0C10.8283 0 21.1946 5.40419 28.7327 13.2062L30.2673 14.7945C45.991 31.0685 72.009 31.0685 87.7327 14.7945L89.2673 13.2062C96.8054 5.40419 107.172 0 118 0C118 0 80.6853 0 0 0Z\"\n fill=\"#F6EFE7\"\n />\n </svg>\n);\n\nexport const ChatWindow = forwardRef<ChatWindowHandle, ChatWindowProps>(function ChatWindow(\n {\n title = 'Ask me a question',\n greeting,\n messages,\n inputValue,\n inputPlaceholder,\n isTyping = false,\n onInputChange,\n onSubmit,\n onClose,\n onAudioClick,\n logo,\n onFeedbackRate,\n csatLabels,\n csatRenderRatingLabel,\n csatThankYouLabels,\n onQuoteCtaClick,\n className,\n ...props\n },\n ref,\n) {\n const logRef = useRef<HTMLDivElement>(null);\n const [showCsat, setShowCsat] = useState(false);\n const [csatRatingMessage, setCsatRatingMessage] = useState<string | null>(null);\n const [quoteGroups, setQuoteGroups] = useState<ChatWindowQuoteItem[][]>([]);\n\n useImperativeHandle(ref, () => ({\n askForFeedback() {\n setShowCsat(true);\n },\n showQuotes(quotes: ChatWindowQuoteItem[]) {\n setQuoteGroups((prev) => [...prev, quotes]);\n },\n }));\n\n useEffect(() => {\n const el = logRef.current;\n if (el) el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' });\n }, [messages, isTyping, showCsat, csatRatingMessage, quoteGroups]);\n\n function handleRate(rating: number) {\n const label = csatRenderRatingLabel ? String(csatRenderRatingLabel(rating)) : String(rating);\n setCsatRatingMessage(label);\n onFeedbackRate?.(rating);\n }\n\n const csatReady = showCsat && csatLabels && csatRenderRatingLabel;\n\n return (\n <div {...props} className={twMerge('flex flex-col gap-20 items-center', className)}>\n <div className=\"chat-border bg-white flex flex-col h-full w-full rounded-32 overflow-hidden\">\n <div className=\"relative flex flex-col items-center shrink-0 pt-4 px-4 h-133 overflow-hidden\">\n <div className=\"absolute inset-x-4 top-4 h-102 bg-lightSand rounded-[24]\">\n {headerCurve}\n </div>\n <div className=\"relative flex flex-col flex-1 items-center justify-end pb-8 w-full z-10\">\n <div className=\"flex items-center justify-center px-12 py-8 w-full\">\n <p className=\"font-serif font-bold italic text-h5 text-center text-black line-clamp-2\">\n {title}\n </p>\n </div>\n {logo ?? (\n <button\n type=\"button\"\n aria-label=\"AI assistant\"\n className=\"chat-border flex items-center justify-center size-48 rounded-full bg-white\"\n >\n <Icon name=\"Sparkles\" width=\"24px\" />\n </button>\n )}\n </div>\n </div>\n\n <div\n ref={logRef}\n role=\"log\"\n aria-label=\"Chat messages\"\n aria-live=\"polite\"\n className=\"chat-message-log flex flex-col flex-1 gap-20 overflow-y-auto py-20\"\n >\n {greeting ? (\n <div className=\"px-20\">\n <p className=\"text-b3 text-black\">{greeting}</p>\n </div>\n ) : null}\n {messages.map((msg) => (\n <ChatMessage key={msg.id} sender={msg.sender}>\n {msg.content}\n </ChatMessage>\n ))}\n {isTyping && (\n <div className=\"px-12\">\n <ChatTypingIndicator />\n </div>\n )}\n {csatReady && (\n <ChatWindowCsatPrompt\n onRate={handleRate}\n labels={csatLabels}\n renderRatingLabel={csatRenderRatingLabel}\n />\n )}\n {csatRatingMessage && <ChatMessage sender=\"user\">{csatRatingMessage}</ChatMessage>}\n {csatRatingMessage && csatThankYouLabels && (\n <ChatWindowCsatThankYou labels={csatThankYouLabels} />\n )}\n {quoteGroups.map((quotes, idx) => (\n <ChatWindowQuote key={idx} quotes={quotes} onCtaClick={onQuoteCtaClick} />\n ))}\n </div>\n\n <div className=\"shrink-0 px-8 pb-8 pt-12\">\n <ChatInput\n value={inputValue}\n placeholder={inputPlaceholder}\n onValueChange={onInputChange}\n onSubmit={onSubmit}\n onAudioClick={onAudioClick}\n />\n </div>\n </div>\n\n {onClose && (\n <button\n type=\"button\"\n aria-label=\"Close chat\"\n onClick={onClose}\n className=\"flex items-center justify-center size-48 rounded-full bg-black border border-black text-white shrink-0 hover:opacity-80 transition-opacity\"\n >\n <Icon name=\"CrossDefault\" width=\"24px\" />\n </button>\n )}\n </div>\n );\n});\n"],"mappings":";;;;;;;;;;;;AAiDA,IAAM,IACJ,kBAAC,OAAD;CACE,WAAU;CACV,OAAM;CACN,QAAO;CACP,SAAQ;WAER,kBAAC,QAAD;EACE,GAAE;EACF,MAAK;EACL,CAAA;CACE,CAAA,EAGK,IAAa,EAA8C,SACtE,EACE,WAAQ,qBACR,aACA,aACA,eACA,qBACA,cAAW,IACX,kBACA,aACA,YACA,iBACA,SACA,mBACA,eACA,0BACA,uBACA,oBACA,cACA,GAAG,KAEL,GACA;CACA,IAAM,IAAS,EAAuB,KAAK,EACrC,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAmB,KAAwB,EAAwB,KAAK,EACzE,CAAC,GAAa,KAAkB,EAAkC,EAAE,CAAC;AAW3E,CATA,EAAoB,UAAY;EAC9B,iBAAiB;AACf,KAAY,GAAK;;EAEnB,WAAW,GAA+B;AACxC,MAAgB,MAAS,CAAC,GAAG,GAAM,EAAO,CAAC;;EAE9C,EAAE,EAEH,QAAgB;EACd,IAAM,IAAK,EAAO;AAClB,EAAI,KAAI,EAAG,SAAS;GAAE,KAAK,EAAG;GAAc,UAAU;GAAU,CAAC;IAChE;EAAC;EAAU;EAAU;EAAU;EAAmB;EAAY,CAAC;CAElE,SAAS,EAAW,GAAgB;AAGlC,EADA,EADsC,OAAxB,IAA+B,EAAsB,EAAO,GAAW,EAAO,CACjE,EAC3B,IAAiB,EAAO;;CAG1B,IAAM,IAAY,KAAY,KAAc;AAE5C,QACE,kBAAC,OAAD;EAAK,GAAI;EAAO,WAAW,EAAQ,qCAAqC,EAAU;YAAlF,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACZ;MACG,CAAA,EACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,KAAD;QAAG,WAAU;kBACV;QACC,CAAA;OACA,CAAA,EACL,KACC,kBAAC,UAAD;OACE,MAAK;OACL,cAAW;OACX,WAAU;iBAEV,kBAAC,GAAD;QAAM,MAAK;QAAW,OAAM;QAAS,CAAA;OAC9B,CAAA,CAAA;;;IAKf,kBAAC,OAAD;KACE,KAAK;KACL,MAAK;KACL,cAAW;KACX,aAAU;KACV,WAAU;eALZ;MAOG,IACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,KAAD;QAAG,WAAU;kBAAsB;QAAa,CAAA;OAC5C,CAAA,GACJ;MACH,EAAS,KAAK,MACb,kBAAC,GAAD;OAA0B,QAAQ,EAAI;iBACnC,EAAI;OACO,EAFI,EAAI,GAER,CACd;MACD,KACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAuB,CAAA;OACnB,CAAA;MAEP,KACC,kBAAC,GAAD;OACE,QAAQ;OACR,QAAQ;OACR,mBAAmB;OACnB,CAAA;MAEH,KAAqB,kBAAC,GAAD;OAAa,QAAO;iBAAQ;OAAgC,CAAA;MACjF,KAAqB,KACpB,kBAAC,GAAD,EAAwB,QAAQ,GAAsB,CAAA;MAEvD,EAAY,KAAK,GAAQ,MACxB,kBAAC,GAAD;OAAmC;OAAQ,YAAY;OAAmB,EAApD,EAAoD,CAAA;;;IAI9E,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MACE,OAAO;MACP,aAAa;MACb,eAAe;MACL;MACI;MACd,CAAA;KACE,CAAA;;MAGP,KACC,kBAAC,UAAD;GACE,MAAK;GACL,cAAW;GACX,SAAS;GACT,WAAU;aAEV,kBAAC,GAAD;IAAM,MAAK;IAAe,OAAM;IAAS,CAAA;GAClC,CAAA,CAAA;;EAIf"}
|
|
1
|
+
{"version":3,"file":"ChatWindow.js","names":[],"sources":["../../lib/ui/ChatWindow.tsx"],"sourcesContent":["'use client';\n\nimport { Icon } from '@clubmed/trident-icons';\nimport type { HTMLAttributes, ReactNode } from 'react';\nimport { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';\nimport { twMerge } from './helpers/twMerge';\nimport { ChatInput } from './ChatInput';\nimport { ChatMessage } from './ChatMessage';\nimport type { ChatMessageSender } from './ChatMessage';\nimport { ChatTypingIndicator } from './ChatTypingIndicator';\nimport { ChatWindowCsatPrompt } from './ChatWindowCsatPrompt';\nimport type { ChatWindowCsatPromptLabels } from './ChatWindowCsatPrompt';\nimport { ChatWindowCsatThankYou } from './ChatWindowCsatThankYou';\nimport type { ChatWindowCsatThankYouLabels } from './ChatWindowCsatThankYou';\nimport { ChatWindowQuote } from './ChatWindowQuote';\nimport type { ChatWindowQuoteItem } from './ChatWindowQuote';\n\nexport type { ChatWindowCsatPromptLabels, ChatWindowCsatThankYouLabels, ChatWindowQuoteItem };\n\nexport interface ChatWindowMessage {\n id: string;\n sender: ChatMessageSender;\n content: string;\n}\n\nexport interface ChatWindowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {\n title?: string;\n greeting?: string;\n messages: ChatWindowMessage[];\n inputValue: string;\n inputPlaceholder?: string;\n isTyping?: boolean;\n onInputChange: (value: string) => void;\n onSubmit: () => void;\n onClose?: () => void;\n onAudioClick?: () => void;\n logo?: ReactNode;\n onFeedbackRate?: (rating: number) => void;\n csatLabels?: ChatWindowCsatPromptLabels;\n csatRenderRatingLabel?: (rating: number) => ReactNode;\n csatThankYouLabels?: ChatWindowCsatThankYouLabels;\n onQuoteCtaClick?: (quote: ChatWindowQuoteItem) => void;\n}\n\nexport interface ChatWindowHandle {\n askForFeedback: () => void;\n showQuotes: (quotes: ChatWindowQuoteItem[]) => void;\n}\n\nconst headerCurve = (\n <svg\n className=\"absolute top-full inset-x-[50%] -translate-x-1/2\"\n width=\"118\"\n height=\"27\"\n viewBox=\"0 0 118 27\"\n >\n <path\n d=\"M0 0C10.8283 0 21.1946 5.40419 28.7327 13.2062L30.2673 14.7945C45.991 31.0685 72.009 31.0685 87.7327 14.7945L89.2673 13.2062C96.8054 5.40419 107.172 0 118 0C118 0 80.6853 0 0 0Z\"\n fill=\"#F6EFE7\"\n />\n </svg>\n);\n\nexport const ChatWindow = forwardRef<ChatWindowHandle, ChatWindowProps>(function ChatWindow(\n {\n title = 'Ask me a question',\n greeting,\n messages,\n inputValue,\n inputPlaceholder,\n isTyping = false,\n onInputChange,\n onSubmit,\n onClose,\n onAudioClick,\n logo,\n onFeedbackRate,\n csatLabels,\n csatRenderRatingLabel,\n csatThankYouLabels,\n onQuoteCtaClick,\n className,\n ...props\n },\n ref,\n) {\n const logRef = useRef<HTMLDivElement>(null);\n const [showCsat, setShowCsat] = useState(false);\n const [csatRatingMessage, setCsatRatingMessage] = useState<string | null>(null);\n const [quoteGroups, setQuoteGroups] = useState<ChatWindowQuoteItem[][]>([]);\n\n useImperativeHandle(ref, () => ({\n askForFeedback() {\n setShowCsat(true);\n },\n showQuotes(quotes: ChatWindowQuoteItem[]) {\n setQuoteGroups((prev) => [...prev, quotes]);\n },\n }));\n\n useEffect(() => {\n const el = logRef.current;\n if (el) el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' });\n }, [messages, isTyping, showCsat, csatRatingMessage, quoteGroups]);\n\n function handleRate(rating: number) {\n const label = csatRenderRatingLabel ? String(csatRenderRatingLabel(rating)) : String(rating);\n setCsatRatingMessage(label);\n onFeedbackRate?.(rating);\n }\n\n const csatReady = showCsat && csatLabels && csatRenderRatingLabel;\n\n return (\n <div {...props} className={twMerge('flex flex-col gap-20 items-center', className)}>\n <div className=\"chat-border bg-white flex flex-col h-full w-full rounded-32 overflow-hidden\">\n <div className=\"relative flex flex-col items-center shrink-0 pt-4 px-4 h-133 overflow-hidden\">\n <div className=\"absolute inset-x-4 top-4 h-102 bg-lightSand rounded-[24]\">\n {headerCurve}\n </div>\n <div className=\"relative flex flex-col flex-1 items-center justify-end pb-8 w-full z-10\">\n <div className=\"flex items-center justify-center px-12 py-8 w-full\">\n <p className=\"font-serif font-bold italic text-h5 text-center text-black line-clamp-2\">\n {title}\n </p>\n </div>\n {logo ?? (\n <button\n type=\"button\"\n aria-label=\"AI assistant\"\n className=\"chat-border flex items-center justify-center size-48 rounded-full bg-white outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2\"\n >\n <Icon name=\"Sparkles\" width=\"24px\" />\n </button>\n )}\n </div>\n </div>\n\n <div\n ref={logRef}\n role=\"log\"\n aria-label=\"Chat messages\"\n aria-live=\"polite\"\n className=\"chat-message-log flex flex-col flex-1 gap-20 overflow-y-auto py-20\"\n >\n {greeting ? (\n <div className=\"px-20\">\n <p className=\"text-b3 text-black\">{greeting}</p>\n </div>\n ) : null}\n {messages.map((msg) => (\n <ChatMessage key={msg.id} sender={msg.sender}>\n {msg.content}\n </ChatMessage>\n ))}\n {isTyping && (\n <div className=\"px-12\">\n <ChatTypingIndicator />\n </div>\n )}\n {csatReady && (\n <ChatWindowCsatPrompt\n onRate={handleRate}\n labels={csatLabels}\n renderRatingLabel={csatRenderRatingLabel}\n />\n )}\n {csatRatingMessage && <ChatMessage sender=\"user\">{csatRatingMessage}</ChatMessage>}\n {csatRatingMessage && csatThankYouLabels && (\n <ChatWindowCsatThankYou labels={csatThankYouLabels} />\n )}\n {quoteGroups.map((quotes, idx) => (\n <ChatWindowQuote key={idx} quotes={quotes} onCtaClick={onQuoteCtaClick} />\n ))}\n </div>\n\n <div className=\"shrink-0 px-8 pb-8 pt-12\">\n <ChatInput\n value={inputValue}\n placeholder={inputPlaceholder}\n onValueChange={onInputChange}\n onSubmit={onSubmit}\n onAudioClick={onAudioClick}\n />\n </div>\n </div>\n\n {onClose && (\n <button\n type=\"button\"\n aria-label=\"Close chat\"\n onClick={onClose}\n className=\"flex items-center justify-center size-48 rounded-full bg-black border border-black text-white shrink-0 hover:opacity-80 transition-opacity outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2\"\n >\n <Icon name=\"CrossDefault\" width=\"24px\" />\n </button>\n )}\n </div>\n );\n});\n"],"mappings":";;;;;;;;;;;;AAiDA,IAAM,IACJ,kBAAC,OAAD;CACE,WAAU;CACV,OAAM;CACN,QAAO;CACP,SAAQ;WAER,kBAAC,QAAD;EACE,GAAE;EACF,MAAK;EACL,CAAA;CACE,CAAA,EAGK,IAAa,EAA8C,SACtE,EACE,WAAQ,qBACR,aACA,aACA,eACA,qBACA,cAAW,IACX,kBACA,aACA,YACA,iBACA,SACA,mBACA,eACA,0BACA,uBACA,oBACA,cACA,GAAG,KAEL,GACA;CACA,IAAM,IAAS,EAAuB,KAAK,EACrC,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAmB,KAAwB,EAAwB,KAAK,EACzE,CAAC,GAAa,KAAkB,EAAkC,EAAE,CAAC;AAW3E,CATA,EAAoB,UAAY;EAC9B,iBAAiB;AACf,KAAY,GAAK;;EAEnB,WAAW,GAA+B;AACxC,MAAgB,MAAS,CAAC,GAAG,GAAM,EAAO,CAAC;;EAE9C,EAAE,EAEH,QAAgB;EACd,IAAM,IAAK,EAAO;AAClB,EAAI,KAAI,EAAG,SAAS;GAAE,KAAK,EAAG;GAAc,UAAU;GAAU,CAAC;IAChE;EAAC;EAAU;EAAU;EAAU;EAAmB;EAAY,CAAC;CAElE,SAAS,EAAW,GAAgB;AAGlC,EADA,EADsC,OAAxB,IAA+B,EAAsB,EAAO,GAAW,EAAO,CACjE,EAC3B,IAAiB,EAAO;;CAG1B,IAAM,IAAY,KAAY,KAAc;AAE5C,QACE,kBAAC,OAAD;EAAK,GAAI;EAAO,WAAW,EAAQ,qCAAqC,EAAU;YAAlF,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACZ;MACG,CAAA,EACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,KAAD;QAAG,WAAU;kBACV;QACC,CAAA;OACA,CAAA,EACL,KACC,kBAAC,UAAD;OACE,MAAK;OACL,cAAW;OACX,WAAU;iBAEV,kBAAC,GAAD;QAAM,MAAK;QAAW,OAAM;QAAS,CAAA;OAC9B,CAAA,CAAA;;;IAKf,kBAAC,OAAD;KACE,KAAK;KACL,MAAK;KACL,cAAW;KACX,aAAU;KACV,WAAU;eALZ;MAOG,IACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,KAAD;QAAG,WAAU;kBAAsB;QAAa,CAAA;OAC5C,CAAA,GACJ;MACH,EAAS,KAAK,MACb,kBAAC,GAAD;OAA0B,QAAQ,EAAI;iBACnC,EAAI;OACO,EAFI,EAAI,GAER,CACd;MACD,KACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAuB,CAAA;OACnB,CAAA;MAEP,KACC,kBAAC,GAAD;OACE,QAAQ;OACR,QAAQ;OACR,mBAAmB;OACnB,CAAA;MAEH,KAAqB,kBAAC,GAAD;OAAa,QAAO;iBAAQ;OAAgC,CAAA;MACjF,KAAqB,KACpB,kBAAC,GAAD,EAAwB,QAAQ,GAAsB,CAAA;MAEvD,EAAY,KAAK,GAAQ,MACxB,kBAAC,GAAD;OAAmC;OAAQ,YAAY;OAAmB,EAApD,EAAoD,CAAA;;;IAI9E,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MACE,OAAO;MACP,aAAa;MACb,eAAe;MACL;MACI;MACd,CAAA;KACE,CAAA;;MAGP,KACC,kBAAC,UAAD;GACE,MAAK;GACL,cAAW;GACX,SAAS;GACT,WAAU;aAEV,kBAAC,GAAD;IAAM,MAAK;IAAe,OAAM;IAAS,CAAA;GAClC,CAAA,CAAA;;EAIf"}
|
|
@@ -48,7 +48,7 @@ function a({ onRate: a, labels: o, renderRatingLabel: s, className: c }) {
|
|
|
48
48
|
onMouseLeave: () => {
|
|
49
49
|
h || u(0);
|
|
50
50
|
},
|
|
51
|
-
className: e("flex items-center justify-center size-48 rounded-full transition-colors", h ? "cursor-default" : "hover:bg-lightSand"),
|
|
51
|
+
className: e("flex items-center justify-center size-48 rounded-full transition-colors outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2", h ? "cursor-default" : "hover:bg-lightSand"),
|
|
52
52
|
children: /* @__PURE__ */ r(n, {
|
|
53
53
|
name: o ? "StarFilled" : "StarOutlined",
|
|
54
54
|
width: "24px",
|