@mediquo/medicore 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/advisor-contact/advisor-contact.js +1 -1
- package/dist/components/advisor-contact/advisor-contact.js.map +1 -1
- package/dist/components/bottom-bar/bottom-bar.js +1 -1
- package/dist/components/bottom-bar/bottom-bar.js.map +1 -1
- package/dist/components/breadcrumb/breadcrumb.js +1 -1
- package/dist/components/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/components/button/button.d.ts +2 -1
- package/dist/components/button/button.js +8 -4
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/card/card.d.ts +2 -1
- package/dist/components/card/card.js +10 -3
- package/dist/components/card/card.js.map +1 -1
- package/dist/components/card-article/card-article.js +6 -2
- package/dist/components/card-article/card-article.js.map +1 -1
- package/dist/components/card-professional-mini/card-professional-mini.js +1 -1
- package/dist/components/card-professional-mini/card-professional-mini.js.map +1 -1
- package/dist/components/card-program/card-program.js +9 -3
- package/dist/components/card-program/card-program.js.map +1 -1
- package/dist/components/chat/chat-composer.js +1 -1
- package/dist/components/chat/chat-composer.js.map +1 -1
- package/dist/components/chat/chat-conversation-row.js +1 -1
- package/dist/components/chat/chat-conversation-row.js.map +1 -1
- package/dist/components/checkbox/checkbox.js +2 -1
- package/dist/components/checkbox/checkbox.js.map +1 -1
- package/dist/components/chip/chip.js +2 -2
- package/dist/components/chip/chip.js.map +1 -1
- package/dist/components/day-cell/day-cell.js +2 -1
- package/dist/components/day-cell/day-cell.js.map +1 -1
- package/dist/components/hover-arrow/hover-arrow.d.ts +4 -0
- package/dist/components/hover-arrow/hover-arrow.js +18 -0
- package/dist/components/hover-arrow/hover-arrow.js.map +1 -0
- package/dist/components/hover-arrow/index.d.ts +1 -0
- package/dist/components/input/control-styles.d.ts +2 -2
- package/dist/components/input/control-styles.js +2 -2
- package/dist/components/input/control-styles.js.map +1 -1
- package/dist/components/link/link.d.ts +2 -1
- package/dist/components/link/link.js +8 -5
- package/dist/components/link/link.js.map +1 -1
- package/dist/components/list-row/list-row.js +1 -1
- package/dist/components/list-row/list-row.js.map +1 -1
- package/dist/components/menu/menu.js +1 -1
- package/dist/components/menu/menu.js.map +1 -1
- package/dist/components/modal/modal.js +1 -1
- package/dist/components/modal/modal.js.map +1 -1
- package/dist/components/nav-item/nav-item.js +2 -2
- package/dist/components/nav-item/nav-item.js.map +1 -1
- package/dist/components/otp-input/otp-input.js +3 -2
- package/dist/components/otp-input/otp-input.js.map +1 -1
- package/dist/components/radio/radio.js +2 -1
- package/dist/components/radio/radio.js.map +1 -1
- package/dist/components/segmented-control/segmented-control.js +1 -1
- package/dist/components/segmented-control/segmented-control.js.map +1 -1
- package/dist/components/select/select.js +1 -1
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/selection-card/selection-card.js +2 -1
- package/dist/components/selection-card/selection-card.js.map +1 -1
- package/dist/components/switch/switch.js +3 -2
- package/dist/components/switch/switch.js.map +1 -1
- package/dist/components/table/table.js +3 -3
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/tabs/tabs.js +3 -3
- package/dist/components/tabs/tabs.js.map +1 -1
- package/dist/components/time-slot/time-slot.js +2 -1
- package/dist/components/time-slot/time-slot.js.map +1 -1
- package/dist/components/toast/toast.js +1 -1
- package/dist/components/toast/toast.js.map +1 -1
- package/dist/components/tooltip/tooltip.js +1 -1
- package/dist/components/tooltip/tooltip.js.map +1 -1
- package/dist/components/video/video-control-button.js +1 -1
- package/dist/components/video/video-control-button.js.map +1 -1
- package/dist/icons/create-icon.d.ts +1 -0
- package/dist/icons/create-icon.js +7 -1
- package/dist/icons/create-icon.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/styles/tokens.css +3 -0
- package/dist/styles/utilities.css +121 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.js","sources":["../../../src/components/toast/toast.tsx"],"sourcesContent":["import {\n Toast as BaseToast,\n type ToastObject,\n type UseToastManagerReturnValue,\n} from \"@base-ui/react/toast\";\nimport { cva } from \"class-variance-authority\";\nimport {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useMemo,\n} from \"react\";\n\nimport { AlertCircleIcon, CheckCircleIcon, InfoCircleIcon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\nimport { Button } from \"../button\";\n\n/**\n * There is no `warning` on purpose: a warning that matters can't vanish on its own,\n * and one that can vanish wasn't a warning. Use a persistent `Banner`.\n */\nexport type ToastStatus = \"neutral\" | \"success\" | \"error\";\n\nexport interface ToastActionOptions {\n /** Defaults to the MediCore `undo` label (\"Deshacer\"). */\n label?: string;\n /** Runs on click; the toast closes right after. */\n onClick: () => void;\n}\n\nexport interface ToastOptions {\n /**\n * What happened, concrete: \"Cita guardada\", \"No se pudo guardar la cita\". Never\n * \"Operación completada con éxito\" or \"Ha ocurrido un error\".\n */\n message: ReactNode;\n /** `neutral` (default) for system state, `success` in past tense, `error` for what failed. */\n status?: ToastStatus;\n /** Undo for success, retry for errors. Leave it out when there is nothing to do. */\n action?: ToastActionOptions;\n /** The icon follows the status; pass `false` to hide it. */\n icon?: boolean;\n /** Milliseconds before it closes on its own (the provider's default otherwise). `0` keeps it open. */\n timeout?: number;\n /** Passing the id of a visible toast updates it in place. */\n id?: string;\n onClose?: () => void;\n}\n\nexport interface UseToastReturnValue {\n /** Shows a toast and returns its id. */\n toast: (options: ToastOptions) => string;\n /** Closes a toast by id, or all of them. */\n close: (id?: string) => void;\n}\n\ninterface ToastData {\n icon: boolean;\n}\n\ntype ToastApi = Pick<UseToastManagerReturnValue<ToastData>, \"add\" | \"close\">;\n\nconst ToastApiContext = createContext<ToastApi | null>(null);\n\n/**\n * Shows transient notices. Needs a `ToastProvider` above.\n *\n * ```tsx\n * const { toast } = useToast();\n * toast({ message: \"Cita guardada\", status: \"success\", action: { onClick: undo } });\n * ```\n */\nexport function useToast(): UseToastReturnValue {\n const manager = useContext(ToastApiContext);\n const labels = useMediCoreLabels();\n\n if (manager === null) {\n throw new Error(\n \"MediCore: useToast() must be used inside <ToastProvider>.\",\n );\n }\n\n const toast = useCallback(\n ({\n message,\n status = \"neutral\",\n action,\n icon = true,\n ...options\n }: ToastOptions): string => {\n let id = \"\";\n id = manager.add({\n ...options,\n title: message,\n type: status,\n // High priority is announced assertively, so only errors use it.\n priority: status === \"error\" ? \"high\" : \"low\",\n data: { icon },\n actionProps: action && {\n children: action.label ?? labels.undo,\n onClick: () => {\n action.onClick();\n manager.close(id);\n },\n },\n });\n return id;\n },\n [manager, labels.undo],\n );\n\n return useMemo(() => ({ toast, close: manager.close }), [toast, manager]);\n}\n\nexport interface ToastProviderProps {\n children?: ReactNode;\n /** Default time on screen in milliseconds. Hovering or focusing the toasts pauses it. */\n timeout?: number;\n /** Toasts visible at once; older ones are hidden until there is room. */\n limit?: number;\n /** Accessible name of the notifications region. */\n label?: string;\n}\n\n/**\n * Wrap the app once. Renders the toast region: at the bottom on mobile (full width\n * minus 16px per side) and top right on desktop (360px), at most three at a time.\n * The timer pauses on hover and keyboard focus, and F6 jumps into the region.\n */\nexport function ToastProvider({\n children,\n timeout,\n limit = 3,\n label = \"Notificaciones\",\n}: ToastProviderProps): React.JSX.Element {\n return (\n <BaseToast.Provider timeout={timeout} limit={limit}>\n <ToastApiBridge>{children}</ToastApiBridge>\n <Toaster label={label} />\n </BaseToast.Provider>\n );\n}\n\n/**\n * Hands `add`/`close` to `useToast()` without the toast list, so components that only\n * show toasts don't re-render every time one appears or goes away. They talk to the\n * provider's store directly, so a toast added from a mount effect isn't lost.\n */\nfunction ToastApiBridge({\n children,\n}: {\n children?: ReactNode;\n}): React.JSX.Element {\n const { add, close } = BaseToast.useToastManager<ToastData>();\n const api = useMemo(() => ({ add, close }), [add, close]);\n\n return (\n <ToastApiContext.Provider value={api}>{children}</ToastApiContext.Provider>\n );\n}\n\nfunction Toaster({ label }: { label: string }): React.JSX.Element {\n const { toasts } = BaseToast.useToastManager<ToastData>();\n\n return (\n <BaseToast.Portal>\n <BaseToast.Viewport\n data-slot=\"toast-viewport\"\n aria-label={label}\n className={cn(\n \"fixed inset-x-4 bottom-4 z-60 flex flex-col-reverse gap-2 outline-hidden\",\n \"md:inset-x-auto md:top-8 md:right-8 md:bottom-auto md:w-90 md:flex-col\",\n )}\n >\n {toasts.map((toast) => (\n <ToastItem key={toast.id} toast={toast} />\n ))}\n </BaseToast.Viewport>\n </BaseToast.Portal>\n );\n}\n\nconst toastVariants = cva(\n [\n \"flex items-center gap-3 rounded-lg px-4 py-3 text-text-inverse shadow-lg select-none\",\n \"[transform:translateX(var(--toast-swipe-movement-x))_translateY(var(--toast-swipe-movement-y))]\",\n \"transition-[opacity,transform] duration-200 ease-out motion-reduce:transition-none\",\n \"data-starting-style:opacity-0 data-starting-style:[transform:translateY(100%)] md:data-starting-style:[transform:translateY(-100%)]\",\n \"data-ending-style:opacity-0\",\n \"data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]\",\n \"data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]\",\n \"data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))]\",\n \"data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))]\",\n \"data-limited:hidden focus-ring\",\n ],\n {\n variants: {\n status: {\n neutral: \"bg-surface-inverse\",\n // success-STRONG, not the status green: white on #34C759 is 2.22:1.\n success: \"bg-success-strong\",\n error: \"bg-error-strong\",\n },\n },\n defaultVariants: {\n status: \"neutral\",\n },\n },\n);\n\nconst statusIcons = {\n neutral: InfoCircleIcon,\n success: CheckCircleIcon,\n error: AlertCircleIcon,\n} as const;\n\nfunction isToastStatus(value: string | undefined): value is ToastStatus {\n return value === \"neutral\" || value === \"success\" || value === \"error\";\n}\n\nfunction ToastItem({\n toast,\n}: {\n toast: ToastObject<ToastData>;\n}): React.JSX.Element {\n const status = isToastStatus(toast.type) ? toast.type : \"neutral\";\n const Icon = statusIcons[status];\n\n return (\n <BaseToast.Root\n toast={toast}\n data-slot=\"toast\"\n className={toastVariants({ status })}\n >\n {toast.data?.icon !== false && <Icon size=\"md\" className=\"shrink-0\" />}\n <BaseToast.Title\n render={<p />}\n className=\"min-w-0 flex-1 text-body-small\"\n />\n {/* The 36px button overflows the 12px padding so the toast stays 48px tall. */}\n <BaseToast.Action\n render={\n <Button\n variant=\"inverse-ghost\"\n size=\"small\"\n className=\"-my-1.5 underline underline-offset-2\"\n />\n }\n />\n </BaseToast.Root>\n );\n}\n"],"names":["BaseToast"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"toast.js","sources":["../../../src/components/toast/toast.tsx"],"sourcesContent":["import {\n Toast as BaseToast,\n type ToastObject,\n type UseToastManagerReturnValue,\n} from \"@base-ui/react/toast\";\nimport { cva } from \"class-variance-authority\";\nimport {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useMemo,\n} from \"react\";\n\nimport { AlertCircleIcon, CheckCircleIcon, InfoCircleIcon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\nimport { Button } from \"../button\";\n\n/**\n * There is no `warning` on purpose: a warning that matters can't vanish on its own,\n * and one that can vanish wasn't a warning. Use a persistent `Banner`.\n */\nexport type ToastStatus = \"neutral\" | \"success\" | \"error\";\n\nexport interface ToastActionOptions {\n /** Defaults to the MediCore `undo` label (\"Deshacer\"). */\n label?: string;\n /** Runs on click; the toast closes right after. */\n onClick: () => void;\n}\n\nexport interface ToastOptions {\n /**\n * What happened, concrete: \"Cita guardada\", \"No se pudo guardar la cita\". Never\n * \"Operación completada con éxito\" or \"Ha ocurrido un error\".\n */\n message: ReactNode;\n /** `neutral` (default) for system state, `success` in past tense, `error` for what failed. */\n status?: ToastStatus;\n /** Undo for success, retry for errors. Leave it out when there is nothing to do. */\n action?: ToastActionOptions;\n /** The icon follows the status; pass `false` to hide it. */\n icon?: boolean;\n /** Milliseconds before it closes on its own (the provider's default otherwise). `0` keeps it open. */\n timeout?: number;\n /** Passing the id of a visible toast updates it in place. */\n id?: string;\n onClose?: () => void;\n}\n\nexport interface UseToastReturnValue {\n /** Shows a toast and returns its id. */\n toast: (options: ToastOptions) => string;\n /** Closes a toast by id, or all of them. */\n close: (id?: string) => void;\n}\n\ninterface ToastData {\n icon: boolean;\n}\n\ntype ToastApi = Pick<UseToastManagerReturnValue<ToastData>, \"add\" | \"close\">;\n\nconst ToastApiContext = createContext<ToastApi | null>(null);\n\n/**\n * Shows transient notices. Needs a `ToastProvider` above.\n *\n * ```tsx\n * const { toast } = useToast();\n * toast({ message: \"Cita guardada\", status: \"success\", action: { onClick: undo } });\n * ```\n */\nexport function useToast(): UseToastReturnValue {\n const manager = useContext(ToastApiContext);\n const labels = useMediCoreLabels();\n\n if (manager === null) {\n throw new Error(\n \"MediCore: useToast() must be used inside <ToastProvider>.\",\n );\n }\n\n const toast = useCallback(\n ({\n message,\n status = \"neutral\",\n action,\n icon = true,\n ...options\n }: ToastOptions): string => {\n let id = \"\";\n id = manager.add({\n ...options,\n title: message,\n type: status,\n // High priority is announced assertively, so only errors use it.\n priority: status === \"error\" ? \"high\" : \"low\",\n data: { icon },\n actionProps: action && {\n children: action.label ?? labels.undo,\n onClick: () => {\n action.onClick();\n manager.close(id);\n },\n },\n });\n return id;\n },\n [manager, labels.undo],\n );\n\n return useMemo(() => ({ toast, close: manager.close }), [toast, manager]);\n}\n\nexport interface ToastProviderProps {\n children?: ReactNode;\n /** Default time on screen in milliseconds. Hovering or focusing the toasts pauses it. */\n timeout?: number;\n /** Toasts visible at once; older ones are hidden until there is room. */\n limit?: number;\n /** Accessible name of the notifications region. */\n label?: string;\n}\n\n/**\n * Wrap the app once. Renders the toast region: at the bottom on mobile (full width\n * minus 16px per side) and top right on desktop (360px), at most three at a time.\n * The timer pauses on hover and keyboard focus, and F6 jumps into the region.\n */\nexport function ToastProvider({\n children,\n timeout,\n limit = 3,\n label = \"Notificaciones\",\n}: ToastProviderProps): React.JSX.Element {\n return (\n <BaseToast.Provider timeout={timeout} limit={limit}>\n <ToastApiBridge>{children}</ToastApiBridge>\n <Toaster label={label} />\n </BaseToast.Provider>\n );\n}\n\n/**\n * Hands `add`/`close` to `useToast()` without the toast list, so components that only\n * show toasts don't re-render every time one appears or goes away. They talk to the\n * provider's store directly, so a toast added from a mount effect isn't lost.\n */\nfunction ToastApiBridge({\n children,\n}: {\n children?: ReactNode;\n}): React.JSX.Element {\n const { add, close } = BaseToast.useToastManager<ToastData>();\n const api = useMemo(() => ({ add, close }), [add, close]);\n\n return (\n <ToastApiContext.Provider value={api}>{children}</ToastApiContext.Provider>\n );\n}\n\nfunction Toaster({ label }: { label: string }): React.JSX.Element {\n const { toasts } = BaseToast.useToastManager<ToastData>();\n\n return (\n <BaseToast.Portal>\n <BaseToast.Viewport\n data-slot=\"toast-viewport\"\n aria-label={label}\n className={cn(\n \"fixed inset-x-4 bottom-4 z-60 flex flex-col-reverse gap-2 outline-hidden\",\n \"md:inset-x-auto md:top-8 md:right-8 md:bottom-auto md:w-90 md:flex-col\",\n )}\n >\n {toasts.map((toast) => (\n <ToastItem key={toast.id} toast={toast} />\n ))}\n </BaseToast.Viewport>\n </BaseToast.Portal>\n );\n}\n\nconst toastVariants = cva(\n [\n \"flex items-center gap-3 rounded-lg px-4 py-3 text-text-inverse shadow-lg select-none\",\n \"[transform:translateX(var(--toast-swipe-movement-x))_translateY(var(--toast-swipe-movement-y))]\",\n \"transition-[opacity,transform] duration-200 ease-out-soft motion-reduce:transition-none\",\n \"data-starting-style:opacity-0 data-starting-style:[transform:translateY(100%)] md:data-starting-style:[transform:translateY(-100%)]\",\n \"data-ending-style:opacity-0\",\n \"data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]\",\n \"data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]\",\n \"data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))]\",\n \"data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))]\",\n \"data-limited:hidden focus-ring\",\n ],\n {\n variants: {\n status: {\n neutral: \"bg-surface-inverse\",\n // success-STRONG, not the status green: white on #34C759 is 2.22:1.\n success: \"bg-success-strong\",\n error: \"bg-error-strong\",\n },\n },\n defaultVariants: {\n status: \"neutral\",\n },\n },\n);\n\nconst statusIcons = {\n neutral: InfoCircleIcon,\n success: CheckCircleIcon,\n error: AlertCircleIcon,\n} as const;\n\nfunction isToastStatus(value: string | undefined): value is ToastStatus {\n return value === \"neutral\" || value === \"success\" || value === \"error\";\n}\n\nfunction ToastItem({\n toast,\n}: {\n toast: ToastObject<ToastData>;\n}): React.JSX.Element {\n const status = isToastStatus(toast.type) ? toast.type : \"neutral\";\n const Icon = statusIcons[status];\n\n return (\n <BaseToast.Root\n toast={toast}\n data-slot=\"toast\"\n className={toastVariants({ status })}\n >\n {toast.data?.icon !== false && <Icon size=\"md\" className=\"shrink-0\" />}\n <BaseToast.Title\n render={<p />}\n className=\"min-w-0 flex-1 text-body-small\"\n />\n {/* The 36px button overflows the 12px padding so the toast stays 48px tall. */}\n <BaseToast.Action\n render={\n <Button\n variant=\"inverse-ghost\"\n size=\"small\"\n className=\"-my-1.5 underline underline-offset-2\"\n />\n }\n />\n </BaseToast.Root>\n );\n}\n"],"names":["BaseToast"],"mappings":"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"}
|
|
@@ -79,7 +79,7 @@ function TooltipContent({
|
|
|
79
79
|
"data-slot": "tooltip",
|
|
80
80
|
className: cn(
|
|
81
81
|
"max-w-60 rounded-md bg-surface-inverse px-2 py-1.5 text-label-small text-text-inverse",
|
|
82
|
-
"origin-(--transform-origin) transition-[opacity,scale] duration-150 ease-out",
|
|
82
|
+
"origin-(--transform-origin) transition-[opacity,scale] duration-150 ease-out-soft",
|
|
83
83
|
"data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:opacity-0",
|
|
84
84
|
"data-instant:transition-none motion-reduce:transition-none",
|
|
85
85
|
className
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooltip.js","sources":["../../../src/components/tooltip/tooltip.tsx"],"sourcesContent":["import { Tooltip as BaseTooltip } from \"@base-ui/react/tooltip\";\nimport {\n createContext,\n isValidElement,\n type ReactNode,\n useCallback,\n useContext,\n useId,\n useMemo,\n useState,\n} from \"react\";\n\nimport { cn } from \"../../lib/cn\";\n\nexport type TooltipPlacement = \"top\" | \"bottom\";\n\ninterface TooltipContextValue {\n id: string;\n open: boolean;\n}\n\nconst TooltipContext = createContext<TooltipContextValue | null>(null);\n\nfunction useTooltipContext(part: string): TooltipContextValue {\n const context = useContext(TooltipContext);\n if (context === null) {\n throw new Error(`MediCore: <${part}> must be used inside <Tooltip>.`);\n }\n return context;\n}\n\nexport interface TooltipProviderProps extends BaseTooltip.Provider.Props {}\n\n/**\n * Optional, around a toolbar or the whole app: once one tooltip is showing, the\n * neighbouring ones open without waiting for the delay again.\n */\nexport function TooltipProvider(\n props: TooltipProviderProps,\n): React.JSX.Element {\n return <BaseTooltip.Provider {...props} />;\n}\n\nexport interface TooltipProps extends Omit<BaseTooltip.Root.Props, \"children\"> {\n /**\n * Shortcut: the tooltip text. With it, `children` is the trigger element and the\n * content is built for you: `<Tooltip label=\"Editar alergia\"><IconButton … /></Tooltip>`.\n */\n label?: string;\n /** Used with `label`. Top by default; it flips on its own when there's no room. */\n placement?: TooltipPlacement;\n children?: ReactNode;\n}\n\n/**\n * Floating label for controls without visible text — the edit, delete or download\n * IconButtons. Only to NAME, never to explain: there is no hover on mobile, so\n * anything the screen needs to be understood can't live here. Two or three words.\n *\n * It opens on hover and on keyboard focus, closes with Escape, and is linked to the\n * trigger with `aria-describedby`. The trigger still needs its own `aria-label`: the\n * tooltip doesn't replace it.\n */\nexport function Tooltip({\n label,\n placement,\n children,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n ...props\n}: TooltipProps): React.JSX.Element {\n const id = useId();\n // Mirrors Base UI's open state so the trigger only points at the popup while it exists.\n const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);\n const open = openProp ?? uncontrolledOpen;\n\n const handleOpenChange = useCallback<\n NonNullable<BaseTooltip.Root.Props[\"onOpenChange\"]>\n >(\n (nextOpen, eventDetails) => {\n onOpenChange?.(nextOpen, eventDetails);\n if (!eventDetails.isCanceled) {\n setUncontrolledOpen(nextOpen);\n }\n },\n [onOpenChange],\n );\n\n const context = useMemo(() => ({ id, open }), [id, open]);\n\n return (\n <TooltipContext.Provider value={context}>\n <BaseTooltip.Root\n open={openProp}\n defaultOpen={defaultOpen}\n onOpenChange={handleOpenChange}\n {...props}\n >\n {label !== undefined && isValidElement(children) ? (\n <>\n <TooltipTrigger render={children} />\n <TooltipContent placement={placement}>{label}</TooltipContent>\n </>\n ) : (\n children\n )}\n </BaseTooltip.Root>\n </TooltipContext.Provider>\n );\n}\n\nexport interface TooltipTriggerProps extends BaseTooltip.Trigger.Props {}\n\n/** The element the tooltip names. Pass it with `render={<IconButton aria-label=\"…\" />}`. */\nexport function TooltipTrigger({\n \"aria-describedby\": describedBy,\n ...props\n}: TooltipTriggerProps): React.JSX.Element {\n const { id, open } = useTooltipContext(\"TooltipTrigger\");\n const ids = [describedBy, open ? id : undefined].filter(Boolean).join(\" \");\n\n return (\n <BaseTooltip.Trigger\n data-slot=\"tooltip-trigger\"\n aria-describedby={ids === \"\" ? undefined : ids}\n {...props}\n />\n );\n}\n\nexport interface TooltipContentProps\n extends Omit<BaseTooltip.Popup.Props, \"className\"> {\n placement?: TooltipPlacement;\n className?: string;\n}\n\n/** The label: `surface-inverse` with white 12px text (16.27:1) and an arrow. */\nexport function TooltipContent({\n placement = \"top\",\n className,\n children,\n ...props\n}: TooltipContentProps): React.JSX.Element {\n const { id } = useTooltipContext(\"TooltipContent\");\n\n return (\n <BaseTooltip.Portal>\n <BaseTooltip.Positioner side={placement} sideOffset={8} className=\"z-70\">\n <BaseTooltip.Popup\n id={id}\n role=\"tooltip\"\n data-slot=\"tooltip\"\n className={cn(\n \"max-w-60 rounded-md bg-surface-inverse px-2 py-1.5 text-label-small text-text-inverse\",\n \"origin-(--transform-origin) transition-[opacity,scale] duration-150 ease-out\",\n \"data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:opacity-0\",\n \"data-instant:transition-none motion-reduce:transition-none\",\n className,\n )}\n {...props}\n >\n <BaseTooltip.Arrow\n className={cn(\n \"h-1.5 w-2.5 bg-surface-inverse\",\n \"data-[side=top]:top-full data-[side=top]:[clip-path:polygon(0_0,100%_0,50%_100%)]\",\n \"data-[side=bottom]:bottom-full data-[side=bottom]:[clip-path:polygon(50%_0,100%_100%,0_100%)]\",\n )}\n />\n {children}\n </BaseTooltip.Popup>\n </BaseTooltip.Positioner>\n </BaseTooltip.Portal>\n );\n}\n"],"names":["BaseTooltip"],"mappings":";;;;AAqBA,MAAM,iBAAiB,cAA0C,IAAI;AAErE,SAAS,kBAAkB,MAAmC;AAC5D,QAAM,UAAU,WAAW,cAAc;AACzC,MAAI,YAAY,MAAM;AACpB,UAAM,IAAI,MAAM,cAAc,IAAI,kCAAkC;AAAA,EACtE;AACA,SAAO;AACT;AAQO,SAAS,gBACd,OACmB;AACnB,SAAO,oBAACA,UAAY,UAAZ,EAAsB,GAAG,MAAA,CAAO;AAC1C;AAsBO,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN,cAAc;AAAA,EACd;AAAA,EACA,GAAG;AACL,GAAoC;AAClC,QAAM,KAAK,MAAA;AAEX,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,SAAS,WAAW;AACpE,QAAM,OAAO,YAAY;AAEzB,QAAM,mBAAmB;AAAA,IAGvB,CAAC,UAAU,iBAAiB;AAC1B,qBAAe,UAAU,YAAY;AACrC,UAAI,CAAC,aAAa,YAAY;AAC5B,4BAAoB,QAAQ;AAAA,MAC9B;AAAA,IACF;AAAA,IACA,CAAC,YAAY;AAAA,EAAA;AAGf,QAAM,UAAU,QAAQ,OAAO,EAAE,IAAI,SAAS,CAAC,IAAI,IAAI,CAAC;AAExD,SACE,oBAAC,eAAe,UAAf,EAAwB,OAAO,SAC9B,UAAA;AAAA,IAACA,UAAY;AAAA,IAAZ;AAAA,MACC,MAAM;AAAA,MACN;AAAA,MACA,cAAc;AAAA,MACb,GAAG;AAAA,MAEH,UAAA,UAAU,UAAa,eAAe,QAAQ,IAC7C,qBAAA,UAAA,EACE,UAAA;AAAA,QAAA,oBAAC,gBAAA,EAAe,QAAQ,SAAA,CAAU;AAAA,QAClC,oBAAC,gBAAA,EAAe,WAAuB,UAAA,MAAA,CAAM;AAAA,MAAA,EAAA,CAC/C,IAEA;AAAA,IAAA;AAAA,EAAA,GAGN;AAEJ;AAKO,SAAS,eAAe;AAAA,EAC7B,oBAAoB;AAAA,EACpB,GAAG;AACL,GAA2C;AACzC,QAAM,EAAE,IAAI,SAAS,kBAAkB,gBAAgB;AACvD,QAAM,MAAM,CAAC,aAAa,OAAO,KAAK,MAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAEzE,SACE;AAAA,IAACA,UAAY;AAAA,IAAZ;AAAA,MACC,aAAU;AAAA,MACV,oBAAkB,QAAQ,KAAK,SAAY;AAAA,MAC1C,GAAG;AAAA,IAAA;AAAA,EAAA;AAGV;AASO,SAAS,eAAe;AAAA,EAC7B,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA2C;AACzC,QAAM,EAAE,GAAA,IAAO,kBAAkB,gBAAgB;AAEjD,SACE,oBAACA,UAAY,QAAZ,EACC,UAAA,oBAACA,UAAY,YAAZ,EAAuB,MAAM,WAAW,YAAY,GAAG,WAAU,QAChE,UAAA;AAAA,IAACA,UAAY;AAAA,IAAZ;AAAA,MACC;AAAA,MACA,MAAK;AAAA,MACL,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAA;AAAA,UAACA,UAAY;AAAA,UAAZ;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA;AAAA,YAAA;AAAA,UACF;AAAA,QAAA;AAAA,QAED;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,GAEL,EAAA,CACF;AAEJ;"}
|
|
1
|
+
{"version":3,"file":"tooltip.js","sources":["../../../src/components/tooltip/tooltip.tsx"],"sourcesContent":["import { Tooltip as BaseTooltip } from \"@base-ui/react/tooltip\";\nimport {\n createContext,\n isValidElement,\n type ReactNode,\n useCallback,\n useContext,\n useId,\n useMemo,\n useState,\n} from \"react\";\n\nimport { cn } from \"../../lib/cn\";\n\nexport type TooltipPlacement = \"top\" | \"bottom\";\n\ninterface TooltipContextValue {\n id: string;\n open: boolean;\n}\n\nconst TooltipContext = createContext<TooltipContextValue | null>(null);\n\nfunction useTooltipContext(part: string): TooltipContextValue {\n const context = useContext(TooltipContext);\n if (context === null) {\n throw new Error(`MediCore: <${part}> must be used inside <Tooltip>.`);\n }\n return context;\n}\n\nexport interface TooltipProviderProps extends BaseTooltip.Provider.Props {}\n\n/**\n * Optional, around a toolbar or the whole app: once one tooltip is showing, the\n * neighbouring ones open without waiting for the delay again.\n */\nexport function TooltipProvider(\n props: TooltipProviderProps,\n): React.JSX.Element {\n return <BaseTooltip.Provider {...props} />;\n}\n\nexport interface TooltipProps extends Omit<BaseTooltip.Root.Props, \"children\"> {\n /**\n * Shortcut: the tooltip text. With it, `children` is the trigger element and the\n * content is built for you: `<Tooltip label=\"Editar alergia\"><IconButton … /></Tooltip>`.\n */\n label?: string;\n /** Used with `label`. Top by default; it flips on its own when there's no room. */\n placement?: TooltipPlacement;\n children?: ReactNode;\n}\n\n/**\n * Floating label for controls without visible text — the edit, delete or download\n * IconButtons. Only to NAME, never to explain: there is no hover on mobile, so\n * anything the screen needs to be understood can't live here. Two or three words.\n *\n * It opens on hover and on keyboard focus, closes with Escape, and is linked to the\n * trigger with `aria-describedby`. The trigger still needs its own `aria-label`: the\n * tooltip doesn't replace it.\n */\nexport function Tooltip({\n label,\n placement,\n children,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n ...props\n}: TooltipProps): React.JSX.Element {\n const id = useId();\n // Mirrors Base UI's open state so the trigger only points at the popup while it exists.\n const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);\n const open = openProp ?? uncontrolledOpen;\n\n const handleOpenChange = useCallback<\n NonNullable<BaseTooltip.Root.Props[\"onOpenChange\"]>\n >(\n (nextOpen, eventDetails) => {\n onOpenChange?.(nextOpen, eventDetails);\n if (!eventDetails.isCanceled) {\n setUncontrolledOpen(nextOpen);\n }\n },\n [onOpenChange],\n );\n\n const context = useMemo(() => ({ id, open }), [id, open]);\n\n return (\n <TooltipContext.Provider value={context}>\n <BaseTooltip.Root\n open={openProp}\n defaultOpen={defaultOpen}\n onOpenChange={handleOpenChange}\n {...props}\n >\n {label !== undefined && isValidElement(children) ? (\n <>\n <TooltipTrigger render={children} />\n <TooltipContent placement={placement}>{label}</TooltipContent>\n </>\n ) : (\n children\n )}\n </BaseTooltip.Root>\n </TooltipContext.Provider>\n );\n}\n\nexport interface TooltipTriggerProps extends BaseTooltip.Trigger.Props {}\n\n/** The element the tooltip names. Pass it with `render={<IconButton aria-label=\"…\" />}`. */\nexport function TooltipTrigger({\n \"aria-describedby\": describedBy,\n ...props\n}: TooltipTriggerProps): React.JSX.Element {\n const { id, open } = useTooltipContext(\"TooltipTrigger\");\n const ids = [describedBy, open ? id : undefined].filter(Boolean).join(\" \");\n\n return (\n <BaseTooltip.Trigger\n data-slot=\"tooltip-trigger\"\n aria-describedby={ids === \"\" ? undefined : ids}\n {...props}\n />\n );\n}\n\nexport interface TooltipContentProps\n extends Omit<BaseTooltip.Popup.Props, \"className\"> {\n placement?: TooltipPlacement;\n className?: string;\n}\n\n/** The label: `surface-inverse` with white 12px text (16.27:1) and an arrow. */\nexport function TooltipContent({\n placement = \"top\",\n className,\n children,\n ...props\n}: TooltipContentProps): React.JSX.Element {\n const { id } = useTooltipContext(\"TooltipContent\");\n\n return (\n <BaseTooltip.Portal>\n <BaseTooltip.Positioner side={placement} sideOffset={8} className=\"z-70\">\n <BaseTooltip.Popup\n id={id}\n role=\"tooltip\"\n data-slot=\"tooltip\"\n className={cn(\n \"max-w-60 rounded-md bg-surface-inverse px-2 py-1.5 text-label-small text-text-inverse\",\n \"origin-(--transform-origin) transition-[opacity,scale] duration-150 ease-out-soft\",\n \"data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:opacity-0\",\n \"data-instant:transition-none motion-reduce:transition-none\",\n className,\n )}\n {...props}\n >\n <BaseTooltip.Arrow\n className={cn(\n \"h-1.5 w-2.5 bg-surface-inverse\",\n \"data-[side=top]:top-full data-[side=top]:[clip-path:polygon(0_0,100%_0,50%_100%)]\",\n \"data-[side=bottom]:bottom-full data-[side=bottom]:[clip-path:polygon(50%_0,100%_100%,0_100%)]\",\n )}\n />\n {children}\n </BaseTooltip.Popup>\n </BaseTooltip.Positioner>\n </BaseTooltip.Portal>\n );\n}\n"],"names":["BaseTooltip"],"mappings":";;;;AAqBA,MAAM,iBAAiB,cAA0C,IAAI;AAErE,SAAS,kBAAkB,MAAmC;AAC5D,QAAM,UAAU,WAAW,cAAc;AACzC,MAAI,YAAY,MAAM;AACpB,UAAM,IAAI,MAAM,cAAc,IAAI,kCAAkC;AAAA,EACtE;AACA,SAAO;AACT;AAQO,SAAS,gBACd,OACmB;AACnB,SAAO,oBAACA,UAAY,UAAZ,EAAsB,GAAG,MAAA,CAAO;AAC1C;AAsBO,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN,cAAc;AAAA,EACd;AAAA,EACA,GAAG;AACL,GAAoC;AAClC,QAAM,KAAK,MAAA;AAEX,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,SAAS,WAAW;AACpE,QAAM,OAAO,YAAY;AAEzB,QAAM,mBAAmB;AAAA,IAGvB,CAAC,UAAU,iBAAiB;AAC1B,qBAAe,UAAU,YAAY;AACrC,UAAI,CAAC,aAAa,YAAY;AAC5B,4BAAoB,QAAQ;AAAA,MAC9B;AAAA,IACF;AAAA,IACA,CAAC,YAAY;AAAA,EAAA;AAGf,QAAM,UAAU,QAAQ,OAAO,EAAE,IAAI,SAAS,CAAC,IAAI,IAAI,CAAC;AAExD,SACE,oBAAC,eAAe,UAAf,EAAwB,OAAO,SAC9B,UAAA;AAAA,IAACA,UAAY;AAAA,IAAZ;AAAA,MACC,MAAM;AAAA,MACN;AAAA,MACA,cAAc;AAAA,MACb,GAAG;AAAA,MAEH,UAAA,UAAU,UAAa,eAAe,QAAQ,IAC7C,qBAAA,UAAA,EACE,UAAA;AAAA,QAAA,oBAAC,gBAAA,EAAe,QAAQ,SAAA,CAAU;AAAA,QAClC,oBAAC,gBAAA,EAAe,WAAuB,UAAA,MAAA,CAAM;AAAA,MAAA,EAAA,CAC/C,IAEA;AAAA,IAAA;AAAA,EAAA,GAGN;AAEJ;AAKO,SAAS,eAAe;AAAA,EAC7B,oBAAoB;AAAA,EACpB,GAAG;AACL,GAA2C;AACzC,QAAM,EAAE,IAAI,SAAS,kBAAkB,gBAAgB;AACvD,QAAM,MAAM,CAAC,aAAa,OAAO,KAAK,MAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAEzE,SACE;AAAA,IAACA,UAAY;AAAA,IAAZ;AAAA,MACC,aAAU;AAAA,MACV,oBAAkB,QAAQ,KAAK,SAAY;AAAA,MAC1C,GAAG;AAAA,IAAA;AAAA,EAAA;AAGV;AASO,SAAS,eAAe;AAAA,EAC7B,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA2C;AACzC,QAAM,EAAE,GAAA,IAAO,kBAAkB,gBAAgB;AAEjD,SACE,oBAACA,UAAY,QAAZ,EACC,UAAA,oBAACA,UAAY,YAAZ,EAAuB,MAAM,WAAW,YAAY,GAAG,WAAU,QAChE,UAAA;AAAA,IAACA,UAAY;AAAA,IAAZ;AAAA,MACC;AAAA,MACA,MAAK;AAAA,MACL,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAA;AAAA,UAACA,UAAY;AAAA,UAAZ;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA;AAAA,YAAA;AAAA,UACF;AAAA,QAAA;AAAA,QAED;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,GAEL,EAAA,CACF;AAEJ;"}
|
|
@@ -13,7 +13,7 @@ import { useMediCoreLabels } from "../../provider/medicore-provider.js";
|
|
|
13
13
|
const videoControlButtonVariants = cva(
|
|
14
14
|
[
|
|
15
15
|
"relative inline-flex size-11 shrink-0 cursor-pointer items-center justify-center rounded-xl",
|
|
16
|
-
"text-text-inverse
|
|
16
|
+
"text-text-inverse interactive pressable focus-ring [&_svg]:size-icon-md",
|
|
17
17
|
"data-disabled:cursor-not-allowed"
|
|
18
18
|
],
|
|
19
19
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-control-button.js","sources":["../../../src/components/video/video-control-button.tsx"],"sourcesContent":["import { Button as BaseButton } from \"@base-ui/react/button\";\nimport { Toggle } from \"@base-ui/react/toggle\";\nimport { cva } from \"class-variance-authority\";\nimport { type ComponentProps, useState } from \"react\";\n\nimport {\n MessageIcon,\n Microphone01Icon,\n MicrophoneOff01Icon,\n VideoRecorderIcon,\n VideoRecorderOffIcon,\n} from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\n\nexport type VideoControl = \"microphone\" | \"camera\" | \"chat\";\n\n/** `on`: the device works · `off`: muted microphone / camera turned off. */\nexport type VideoControlState = \"on\" | \"off\";\n\nexport const videoControlButtonVariants = cva(\n [\n \"relative inline-flex size-11 shrink-0 cursor-pointer items-center justify-center rounded-xl\",\n \"text-text-inverse
|
|
1
|
+
{"version":3,"file":"video-control-button.js","sources":["../../../src/components/video/video-control-button.tsx"],"sourcesContent":["import { Button as BaseButton } from \"@base-ui/react/button\";\nimport { Toggle } from \"@base-ui/react/toggle\";\nimport { cva } from \"class-variance-authority\";\nimport { type ComponentProps, useState } from \"react\";\n\nimport {\n MessageIcon,\n Microphone01Icon,\n MicrophoneOff01Icon,\n VideoRecorderIcon,\n VideoRecorderOffIcon,\n} from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\n\nexport type VideoControl = \"microphone\" | \"camera\" | \"chat\";\n\n/** `on`: the device works · `off`: muted microphone / camera turned off. */\nexport type VideoControlState = \"on\" | \"off\";\n\nexport const videoControlButtonVariants = cva(\n [\n \"relative inline-flex size-11 shrink-0 cursor-pointer items-center justify-center rounded-xl\",\n \"text-text-inverse interactive pressable focus-ring [&_svg]:size-icon-md\",\n \"data-disabled:cursor-not-allowed\",\n ],\n {\n variants: {\n state: {\n on: \"border border-border-inverse bg-surface-inverse\",\n off: \"bg-error-strong\",\n },\n },\n defaultVariants: {\n state: \"on\",\n },\n },\n);\n\nexport interface VideoControlButtonProps\n extends Omit<ComponentProps<\"button\">, \"children\" | \"value\"> {\n control: VideoControl;\n /**\n * Microphone and camera. Controlled: pass it with `onStateChange`. `chat` has no\n * off state: it opens the chat.\n */\n state?: VideoControlState;\n /** Uncontrolled initial state. Default `on`. */\n defaultState?: VideoControlState;\n onStateChange?: (state: VideoControlState) => void;\n /** `chat` only: new messages. Shows the red dot and says it in the button's name. */\n unreadCount?: number;\n}\n\n/**\n * Button over the video: microphone, camera and (on mobile) chat. 44×44 drawn:\n * over the video there is no label next to it to extend the hit area.\n *\n * - On: `surface-inverse` fill. Off: `error-strong` fill AND the crossed-out icon —\n * colour is never the only cue. White icon in both, over any image (the button\n * is opaque).\n * - Microphone and camera are toggle buttons (`aria-pressed`, pressed = muted /\n * camera off) and their name says the action: \"Silenciar micrófono\" / \"Activar\n * micrófono\", \"Apagar cámara\" / \"Encender cámara\".\n * - Chat is a plain button: \"Abrir chat, 1 mensaje nuevo\".\n */\nexport function VideoControlButton({\n control,\n state: stateProp,\n defaultState = \"on\",\n onStateChange,\n unreadCount = 0,\n className,\n type = \"button\",\n ...props\n}: VideoControlButtonProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n\n if (control === \"chat\") {\n const unread = unreadCount > 0;\n return (\n <BaseButton\n data-slot=\"video-control-button\"\n data-control={control}\n type={type}\n aria-label={\n unread\n ? `${labels.videoOpenChat}, ${labels.chatNewMessages(unreadCount)}`\n : labels.videoOpenChat\n }\n className={cn(videoControlButtonVariants({ state: \"on\" }), className)}\n {...props}\n >\n <MessageIcon />\n {unread && (\n <span\n data-slot=\"video-control-button-unread\"\n className=\"absolute top-1 right-1 size-2.5 rounded-full bg-error ring-2 ring-text-inverse\"\n />\n )}\n </BaseButton>\n );\n }\n\n return (\n <VideoToggle\n control={control}\n state={stateProp}\n defaultState={defaultState}\n onStateChange={onStateChange}\n className={className}\n type={type}\n {...props}\n />\n );\n}\n\ninterface VideoToggleProps\n extends Omit<VideoControlButtonProps, \"control\" | \"unreadCount\"> {\n control: Exclude<VideoControl, \"chat\">;\n defaultState: VideoControlState;\n}\n\nfunction VideoToggle({\n control,\n state: stateProp,\n defaultState,\n onStateChange,\n className,\n ...props\n}: VideoToggleProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n const controlled = stateProp !== undefined;\n const [uncontrolledState, setUncontrolledState] = useState(defaultState);\n const state = stateProp ?? uncontrolledState;\n const off = state === \"off\";\n\n const label =\n control === \"microphone\"\n ? off\n ? labels.videoUnmuteMicrophone\n : labels.videoMuteMicrophone\n : off\n ? labels.videoTurnOnCamera\n : labels.videoTurnOffCamera;\n\n const Icon =\n control === \"microphone\"\n ? off\n ? MicrophoneOff01Icon\n : Microphone01Icon\n : off\n ? VideoRecorderOffIcon\n : VideoRecorderIcon;\n\n return (\n <Toggle\n data-slot=\"video-control-button\"\n data-control={control}\n data-state={state}\n pressed={off}\n onPressedChange={(pressed) => {\n const next: VideoControlState = pressed ? \"off\" : \"on\";\n if (!controlled) {\n setUncontrolledState(next);\n }\n onStateChange?.(next);\n }}\n aria-label={label}\n className={cn(videoControlButtonVariants({ state }), className)}\n {...props}\n >\n <Icon />\n </Toggle>\n );\n}\n"],"names":["BaseButton"],"mappings":";;;;;;;;;;;;AAoBO,MAAM,6BAA6B;AAAA,EACxC;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAAA,EAEF;AAAA,IACE,UAAU;AAAA,MACR,OAAO;AAAA,QACL,IAAI;AAAA,QACJ,KAAK;AAAA,MAAA;AAAA,IACP;AAAA,IAEF,iBAAiB;AAAA,MACf,OAAO;AAAA,IAAA;AAAA,EACT;AAEJ;AA6BO,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA,OAAO;AAAA,EACP,eAAe;AAAA,EACf;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA,OAAO;AAAA,EACP,GAAG;AACL,GAA+C;AAC7C,QAAM,SAAS,kBAAA;AAEf,MAAI,YAAY,QAAQ;AACtB,UAAM,SAAS,cAAc;AAC7B,WACE;AAAA,MAACA;AAAAA,MAAA;AAAA,QACC,aAAU;AAAA,QACV,gBAAc;AAAA,QACd;AAAA,QACA,cACE,SACI,GAAG,OAAO,aAAa,KAAK,OAAO,gBAAgB,WAAW,CAAC,KAC/D,OAAO;AAAA,QAEb,WAAW,GAAG,2BAA2B,EAAE,OAAO,KAAA,CAAM,GAAG,SAAS;AAAA,QACnE,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAA,oBAAC,aAAA,EAAY;AAAA,UACZ,UACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,aAAU;AAAA,cACV,WAAU;AAAA,YAAA;AAAA,UAAA;AAAA,QACZ;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AAEA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACC,GAAG;AAAA,IAAA;AAAA,EAAA;AAGV;AAQA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAwC;AACtC,QAAM,SAAS,kBAAA;AACf,QAAM,aAAa,cAAc;AACjC,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,SAAS,YAAY;AACvE,QAAM,QAAQ,aAAa;AAC3B,QAAM,MAAM,UAAU;AAEtB,QAAM,QACJ,YAAY,eACR,MACE,OAAO,wBACP,OAAO,sBACT,MACE,OAAO,oBACP,OAAO;AAEf,QAAM,OACJ,YAAY,eACR,MACE,sBACA,mBACF,MACE,uBACA;AAER,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,gBAAc;AAAA,MACd,cAAY;AAAA,MACZ,SAAS;AAAA,MACT,iBAAiB,CAAC,YAAY;AAC5B,cAAM,OAA0B,UAAU,QAAQ;AAClD,YAAI,CAAC,YAAY;AACf,+BAAqB,IAAI;AAAA,QAC3B;AACA,wBAAgB,IAAI;AAAA,MACtB;AAAA,MACA,cAAY;AAAA,MACZ,WAAW,GAAG,2BAA2B,EAAE,MAAA,CAAO,GAAG,SAAS;AAAA,MAC7D,GAAG;AAAA,MAEJ,8BAAC,MAAA,CAAA,CAAK;AAAA,IAAA;AAAA,EAAA;AAGZ;"}
|
|
@@ -5,6 +5,7 @@ export interface IconProps extends Omit<React.SVGProps<SVGSVGElement>, "children
|
|
|
5
5
|
size?: IconSize;
|
|
6
6
|
/** Accessible name. Without it the icon is decorative (aria-hidden). */
|
|
7
7
|
title?: string;
|
|
8
|
+
hoverFill?: boolean;
|
|
8
9
|
}
|
|
9
10
|
/**
|
|
10
11
|
* Builds a MediCore icon: 24×24 grid, 1.5 stroke (Figma `icon-stroke`) in
|
|
@@ -10,6 +10,7 @@ function createIcon(displayName, children) {
|
|
|
10
10
|
function Icon({
|
|
11
11
|
size = "lg",
|
|
12
12
|
title,
|
|
13
|
+
hoverFill = false,
|
|
13
14
|
className,
|
|
14
15
|
...props
|
|
15
16
|
}) {
|
|
@@ -28,7 +29,12 @@ function createIcon(displayName, children) {
|
|
|
28
29
|
"aria-label": labelled ? title : void 0,
|
|
29
30
|
"aria-hidden": labelled ? void 0 : true,
|
|
30
31
|
focusable: labelled ? void 0 : "false",
|
|
31
|
-
className: cn(
|
|
32
|
+
className: cn(
|
|
33
|
+
"shrink-0",
|
|
34
|
+
sizeClasses[size],
|
|
35
|
+
hoverFill && "hover-fill",
|
|
36
|
+
className
|
|
37
|
+
),
|
|
32
38
|
...props,
|
|
33
39
|
children: [
|
|
34
40
|
labelled ? /* @__PURE__ */ jsx("title", { children: title }) : null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-icon.js","sources":["../../src/icons/create-icon.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nimport { cn } from \"../lib/cn\";\n\nexport type IconSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\n\nexport interface IconProps\n extends Omit<React.SVGProps<SVGSVGElement>, \"children\"> {\n /** xs 12 · sm 16 · md 20 · lg 24. Default \"lg\" (the Figma grid). */\n size?: IconSize;\n /** Accessible name. Without it the icon is decorative (aria-hidden). */\n title?: string;\n}\n\nconst sizeClasses: Record<IconSize, string> = {\n xs: \"size-icon-xs\",\n sm: \"size-icon-sm\",\n md: \"size-icon-md\",\n lg: \"size-icon-lg\",\n};\n\n/**\n * Builds a MediCore icon: 24×24 grid, 1.5 stroke (Figma `icon-stroke`) in\n * `currentColor`, so it takes the text colour of its parent — recolour it with a\n * `text-*` token class, never with a hex.\n *\n * Figma: don't go below 16px (`sm`) for stroke icons, the line clogs; `xs` is only\n * for dense chrome. An icon is never the only cue for an action: an icon-only\n * control needs an accessible name (`title` here, or `aria-label` on the control).\n */\nexport function createIcon(\n displayName: string,\n children: ReactNode,\n): (props: IconProps) => React.JSX.Element {\n function Icon({\n size = \"lg\",\n title,\n className,\n ...props\n }: IconProps): React.JSX.Element {\n const labelled = Boolean(title);\n\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n data-slot=\"icon\"\n role={labelled ? \"img\" : undefined}\n aria-label={labelled ? title : undefined}\n aria-hidden={labelled ? undefined : true}\n focusable={labelled ? undefined : \"false\"}\n className={cn(\"shrink-0\"
|
|
1
|
+
{"version":3,"file":"create-icon.js","sources":["../../src/icons/create-icon.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nimport { cn } from \"../lib/cn\";\n\nexport type IconSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\n\nexport interface IconProps\n extends Omit<React.SVGProps<SVGSVGElement>, \"children\"> {\n /** xs 12 · sm 16 · md 20 · lg 24. Default \"lg\" (the Figma grid). */\n size?: IconSize;\n /** Accessible name. Without it the icon is decorative (aria-hidden). */\n title?: string;\n hoverFill?: boolean;\n}\n\nconst sizeClasses: Record<IconSize, string> = {\n xs: \"size-icon-xs\",\n sm: \"size-icon-sm\",\n md: \"size-icon-md\",\n lg: \"size-icon-lg\",\n};\n\n/**\n * Builds a MediCore icon: 24×24 grid, 1.5 stroke (Figma `icon-stroke`) in\n * `currentColor`, so it takes the text colour of its parent — recolour it with a\n * `text-*` token class, never with a hex.\n *\n * Figma: don't go below 16px (`sm`) for stroke icons, the line clogs; `xs` is only\n * for dense chrome. An icon is never the only cue for an action: an icon-only\n * control needs an accessible name (`title` here, or `aria-label` on the control).\n */\nexport function createIcon(\n displayName: string,\n children: ReactNode,\n): (props: IconProps) => React.JSX.Element {\n function Icon({\n size = \"lg\",\n title,\n hoverFill = false,\n className,\n ...props\n }: IconProps): React.JSX.Element {\n const labelled = Boolean(title);\n\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n data-slot=\"icon\"\n role={labelled ? \"img\" : undefined}\n aria-label={labelled ? title : undefined}\n aria-hidden={labelled ? undefined : true}\n focusable={labelled ? undefined : \"false\"}\n className={cn(\n \"shrink-0\",\n sizeClasses[size],\n hoverFill && \"hover-fill\",\n className,\n )}\n {...props}\n >\n {labelled ? <title>{title}</title> : null}\n {children}\n </svg>\n );\n }\n\n Icon.displayName = displayName;\n return Icon;\n}\n"],"names":[],"mappings":";;AAeA,MAAM,cAAwC;AAAA,EAC5C,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAWO,SAAS,WACd,aACA,UACyC;AACzC,WAAS,KAAK;AAAA,IACZ,OAAO;AAAA,IACP;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA,GAAG;AAAA,EAAA,GAC4B;AAC/B,UAAM,WAAW,QAAQ,KAAK;AAE9B,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,QAAO;AAAA,QACP,aAAa;AAAA,QACb,eAAc;AAAA,QACd,gBAAe;AAAA,QACf,aAAU;AAAA,QACV,MAAM,WAAW,QAAQ;AAAA,QACzB,cAAY,WAAW,QAAQ;AAAA,QAC/B,eAAa,WAAW,SAAY;AAAA,QACpC,WAAW,WAAW,SAAY;AAAA,QAClC,WAAW;AAAA,UACT;AAAA,UACA,YAAY,IAAI;AAAA,UAChB,aAAa;AAAA,UACb;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,WAAW,oBAAC,SAAA,EAAO,UAAA,MAAA,CAAM,IAAW;AAAA,UACpC;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AAEA,OAAK,cAAc;AACnB,SAAO;AACT;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export * from "./components/field";
|
|
|
34
34
|
export * from "./components/footer";
|
|
35
35
|
export * from "./components/health-record";
|
|
36
36
|
export * from "./components/hero-greeting";
|
|
37
|
+
export * from "./components/hover-arrow";
|
|
37
38
|
export * from "./components/icon-button";
|
|
38
39
|
export * from "./components/icon-container";
|
|
39
40
|
export * from "./components/included-item";
|
package/dist/index.js
CHANGED
|
@@ -47,6 +47,7 @@ import { Field } from "./components/field/field.js";
|
|
|
47
47
|
import { Footer, FooterLink } from "./components/footer/footer.js";
|
|
48
48
|
import { HealthRecordHeader, HealthRecordRow, HealthRecordSection } from "./components/health-record/health-record.js";
|
|
49
49
|
import { HeroGreeting } from "./components/hero-greeting/hero-greeting.js";
|
|
50
|
+
import { HoverArrow } from "./components/hover-arrow/hover-arrow.js";
|
|
50
51
|
import { IconButton, iconButtonVariants } from "./components/icon-button/icon-button.js";
|
|
51
52
|
import { IconContainer, iconContainerVariants } from "./components/icon-container/icon-container.js";
|
|
52
53
|
import { IncludedItem } from "./components/included-item/included-item.js";
|
|
@@ -158,6 +159,7 @@ export {
|
|
|
158
159
|
HealthRecordRow,
|
|
159
160
|
HealthRecordSection,
|
|
160
161
|
HeroGreeting,
|
|
162
|
+
HoverArrow,
|
|
161
163
|
IconButton,
|
|
162
164
|
IconContainer,
|
|
163
165
|
IncludedItem,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/styles/tokens.css
CHANGED
|
@@ -116,6 +116,9 @@
|
|
|
116
116
|
/* Motion. Always behind motion-safe: in the components. */
|
|
117
117
|
--animate-skeleton-shimmer: skeleton-shimmer 1.2s linear infinite;
|
|
118
118
|
|
|
119
|
+
--ease-out-soft: cubic-bezier(0.2, 0, 0, 1);
|
|
120
|
+
--duration-interactive: 150ms;
|
|
121
|
+
|
|
119
122
|
@keyframes skeleton-shimmer {
|
|
120
123
|
from {
|
|
121
124
|
background-position: 100% 0;
|
|
@@ -35,3 +35,124 @@
|
|
|
35
35
|
transform: translate(-50%, -50%);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
@utility interactive {
|
|
40
|
+
transition-property:
|
|
41
|
+
color, background-color, border-color, outline-color,
|
|
42
|
+
text-decoration-color, box-shadow, opacity, filter, translate, scale;
|
|
43
|
+
transition-duration: var(--duration-interactive);
|
|
44
|
+
transition-timing-function: var(--ease-out-soft);
|
|
45
|
+
|
|
46
|
+
@media (prefers-reduced-motion: reduce) {
|
|
47
|
+
transition-property:
|
|
48
|
+
color, background-color, border-color, outline-color,
|
|
49
|
+
text-decoration-color, box-shadow, opacity, filter;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@utility pressable {
|
|
54
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
55
|
+
&:active:not(:disabled, [data-disabled], [aria-disabled="true"]) {
|
|
56
|
+
scale: 0.97;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@utility hover-lift {
|
|
62
|
+
@media (hover: hover) {
|
|
63
|
+
&:has([data-slot="card-link"]):hover {
|
|
64
|
+
box-shadow: var(--shadow-lg);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
|
|
69
|
+
&:has([data-slot="card-link"]):hover {
|
|
70
|
+
translate: 0 -2px;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@utility hover-zoom-media {
|
|
76
|
+
& img {
|
|
77
|
+
transition: scale 400ms var(--ease-out-soft);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
|
|
81
|
+
&:has([data-slot="card-link"]):hover img {
|
|
82
|
+
scale: 1.04;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@custom-variant hover-triggered {
|
|
88
|
+
@media (hover: hover) {
|
|
89
|
+
:is(
|
|
90
|
+
.hover-trigger,
|
|
91
|
+
.hover-lift:has([data-slot="card-link"])
|
|
92
|
+
):not([data-disabled], [aria-disabled="true"], :disabled):hover
|
|
93
|
+
& {
|
|
94
|
+
@slot;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
:is(.hover-trigger, .hover-lift:has([data-slot="card-link"])):is(
|
|
99
|
+
:focus-visible,
|
|
100
|
+
:has(:focus-visible)
|
|
101
|
+
)
|
|
102
|
+
& {
|
|
103
|
+
@slot;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@utility hover-arrow {
|
|
108
|
+
display: inline-flex;
|
|
109
|
+
vertical-align: middle;
|
|
110
|
+
width: 0;
|
|
111
|
+
overflow: hidden;
|
|
112
|
+
margin-inline-start: calc(-1 * var(--hover-arrow-gap, 0px));
|
|
113
|
+
transition-property: width, margin-inline-start;
|
|
114
|
+
transition-duration: 300ms;
|
|
115
|
+
transition-timing-function: var(--ease-out-soft);
|
|
116
|
+
|
|
117
|
+
& > svg {
|
|
118
|
+
flex-shrink: 0;
|
|
119
|
+
opacity: 0;
|
|
120
|
+
translate: -0.375em 0;
|
|
121
|
+
transition-property: opacity, translate;
|
|
122
|
+
transition-duration: 300ms;
|
|
123
|
+
transition-timing-function: var(--ease-out-soft);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@variant hover-triggered {
|
|
127
|
+
width: var(--hover-arrow-size, 1em);
|
|
128
|
+
margin-inline-start: calc(
|
|
129
|
+
var(--hover-arrow-space, 0.25em) - var(--hover-arrow-gap, 0px)
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
& > svg {
|
|
133
|
+
opacity: 1;
|
|
134
|
+
translate: 0 0;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@media (prefers-reduced-motion: reduce) {
|
|
139
|
+
transition-property: none;
|
|
140
|
+
|
|
141
|
+
& > svg {
|
|
142
|
+
translate: 0 0;
|
|
143
|
+
transition-property: opacity;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@utility hover-fill {
|
|
149
|
+
fill: color-mix(in oklab, currentColor 0%, transparent);
|
|
150
|
+
transition: fill 300ms var(--ease-out-soft);
|
|
151
|
+
|
|
152
|
+
@variant hover-triggered {
|
|
153
|
+
fill: var(
|
|
154
|
+
--hover-fill-color,
|
|
155
|
+
color-mix(in oklab, currentColor 20%, transparent)
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|