@langchain/macaw-components 1.0.0-beta.6 → 1.0.0-beta.8
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/_chunks/{LoadingIndicator-CDcArsiM.js → LoadingIndicator-DujSAzqj.js} +2 -1
- package/dist/_chunks/LoadingIndicator-DujSAzqj.js.map +1 -0
- package/dist/catalog.json +499 -469
- package/dist/components/Attachment/Attachment.d.ts +2 -0
- package/dist/components/Attachment/Attachment.js +10 -5
- package/dist/components/Attachment/Attachment.js.map +1 -1
- package/dist/components/HeatmapChart/HeatmapBlock.d.ts +22 -0
- package/dist/components/HeatmapChart/HeatmapBlock.js +54 -0
- package/dist/components/HeatmapChart/HeatmapBlock.js.map +1 -0
- package/dist/components/HeatmapChart/HeatmapChart.d.ts +9 -0
- package/dist/components/HeatmapChart/HeatmapChart.js +79 -0
- package/dist/components/HeatmapChart/HeatmapChart.js.map +1 -0
- package/dist/components/HeatmapChart/index.d.ts +4 -0
- package/dist/components/HeatmapChart/index.js +3 -0
- package/dist/components/ThinkingState/LoadingIndicator.d.ts +2 -1
- package/dist/components/ThinkingState/LoadingIndicator.js +1 -1
- package/dist/components/ThinkingState/ThinkingState.js +4 -4
- package/dist/components/ThinkingState/ThinkingState.js.map +1 -1
- package/dist/components/ThinkingState/ThinkingStateTimer.d.ts +1 -1
- package/dist/components/ThinkingState/ThinkingStateTimer.js +1 -1
- package/dist/components/ThinkingState/ThinkingStateTimer.js.map +1 -1
- package/dist/components/ThinkingState/index.js +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +3 -1
- package/dist/index.js +3 -1
- package/dist/utilities.css +1 -1
- package/dist/utils/spacing.d.ts +1 -1
- package/dist/utils/spacing.js +1 -1
- package/dist/utils/spacing.js.map +1 -1
- package/docs/DESIGN.md +4 -1
- package/package.json +10 -2
- package/dist/_chunks/LoadingIndicator-CDcArsiM.js.map +0 -1
|
@@ -12,6 +12,8 @@ export interface AttachmentProps extends Omit<ComponentPropsWithRef<'div'>, 'chi
|
|
|
12
12
|
/** File-row height: sm=24px, md=32px, lg=48px. Thumbnails use larger tiles. */
|
|
13
13
|
size?: 'sm' | 'md' | 'lg';
|
|
14
14
|
status?: 'ready' | 'loading' | 'uploading' | 'error';
|
|
15
|
+
/** Disables opening, removal, and custom actions. */
|
|
16
|
+
disabled?: boolean;
|
|
15
17
|
onOpen?: () => void;
|
|
16
18
|
openLabel?: string;
|
|
17
19
|
onRemove?: () => void;
|
|
@@ -32,7 +32,7 @@ var SIZE_STYLES = {
|
|
|
32
32
|
thumbnail: "w-40"
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
|
-
var Attachment = forwardRef(function Attachment({ name, contentType, icon, preview, metadata, variant = "file", size = "md", status = "ready", onOpen, openLabel = `View ${name}`, onRemove, actions, className, ...props }, ref) {
|
|
35
|
+
var Attachment = forwardRef(function Attachment({ name, contentType, icon, preview, metadata, variant = "file", size = "md", status = "ready", disabled = false, onOpen, openLabel = `View ${name}`, onRemove, actions, className, ...props }, ref) {
|
|
36
36
|
const statusId = useId();
|
|
37
37
|
const sizeStyles = SIZE_STYLES[size];
|
|
38
38
|
const isThumbnail = variant === "thumbnail";
|
|
@@ -47,6 +47,7 @@ var Attachment = forwardRef(function Attachment({ name, contentType, icon, previ
|
|
|
47
47
|
iconWeight: "regular",
|
|
48
48
|
iconClassName: isThumbnail ? size === "sm" ? "size-2.5" : "size-3" : size === "sm" ? "size-3" : "size-3.5",
|
|
49
49
|
label: `Remove ${name}`,
|
|
50
|
+
disabled,
|
|
50
51
|
color: "secondary",
|
|
51
52
|
variant: isThumbnail ? "normal" : "plain",
|
|
52
53
|
round: isThumbnail,
|
|
@@ -60,7 +61,7 @@ var Attachment = forwardRef(function Attachment({ name, contentType, icon, previ
|
|
|
60
61
|
size: size === "sm" ? "xxs" : size === "md" ? "xs" : "sm",
|
|
61
62
|
onClick: (event) => {
|
|
62
63
|
event.stopPropagation();
|
|
63
|
-
onRemove();
|
|
64
|
+
if (!disabled) onRemove();
|
|
64
65
|
}
|
|
65
66
|
}) : null;
|
|
66
67
|
const media = hasPreview ? preview : /* @__PURE__ */ jsx(Icon, {
|
|
@@ -124,8 +125,9 @@ var Attachment = forwardRef(function Attachment({ name, contentType, icon, previ
|
|
|
124
125
|
role: "group",
|
|
125
126
|
"aria-label": name,
|
|
126
127
|
...props,
|
|
128
|
+
"aria-disabled": disabled || void 0,
|
|
127
129
|
onClick: (event) => {
|
|
128
|
-
if (onOpen && event.target === event.currentTarget) {
|
|
130
|
+
if (!disabled && onOpen && event.target === event.currentTarget) {
|
|
129
131
|
event.stopPropagation();
|
|
130
132
|
onOpen();
|
|
131
133
|
}
|
|
@@ -134,23 +136,26 @@ var Attachment = forwardRef(function Attachment({ name, contentType, icon, previ
|
|
|
134
136
|
sizeStyles.thumbnail,
|
|
135
137
|
"group/attachment relative flex-col items-stretch",
|
|
136
138
|
size === "sm" ? "p-space-1" : "p-space-2"
|
|
137
|
-
] : [sizeStyles.height, sizeStyles.padding], status === "error" && "border-error", onOpen && "cursor-pointer", className),
|
|
139
|
+
] : [sizeStyles.height, sizeStyles.padding], status === "error" && "border-error", onOpen && !disabled && "cursor-pointer", disabled && "cursor-not-allowed opacity-50", className),
|
|
138
140
|
children: [
|
|
139
141
|
onOpen ? /* @__PURE__ */ jsx(Button, {
|
|
140
142
|
type: "button",
|
|
141
143
|
color: "secondary",
|
|
142
144
|
variant: "plain",
|
|
143
145
|
"aria-label": openLabel,
|
|
146
|
+
disabled,
|
|
144
147
|
"aria-describedby": description ? statusId : void 0,
|
|
145
148
|
className: cn("min-w-0 flex-1 justify-start p-0 text-left", isThumbnail ? "h-auto w-full max-w-full flex-none whitespace-normal" : "h-full"),
|
|
146
149
|
onClick: (event) => {
|
|
147
150
|
event.stopPropagation();
|
|
148
|
-
onOpen();
|
|
151
|
+
if (!disabled) onOpen();
|
|
149
152
|
},
|
|
150
153
|
children: content
|
|
151
154
|
}) : content,
|
|
152
155
|
isThumbnail && removeButton,
|
|
153
156
|
(actions != null || !isThumbnail && onRemove) && /* @__PURE__ */ jsxs("fieldset", {
|
|
157
|
+
disabled,
|
|
158
|
+
inert: disabled || void 0,
|
|
154
159
|
"aria-label": `Actions for ${name}`,
|
|
155
160
|
className: cn("m-0 flex min-w-0 max-w-full shrink-0 items-center justify-end gap-space-1 border-0 p-0", isThumbnail && "flex-wrap"),
|
|
156
161
|
onClick: (event) => event.stopPropagation(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Attachment.js","names":[],"sources":["../../../src/components/Attachment/Attachment.tsx"],"sourcesContent":["import type { ComponentPropsWithRef, ReactNode } from 'react';\nimport { forwardRef, useId } from 'react';\n\nimport { FileIcon } from '@phosphor-icons/react/dist/ssr/File';\nimport { WarningCircleIcon } from '@phosphor-icons/react/dist/ssr/WarningCircle';\nimport { XIcon } from '@phosphor-icons/react/dist/ssr/X';\n\nimport { cn } from '../../utils/cn';\nimport {\n CONTROL_SIZES,\n VISUAL_ELEMENT_SIZES,\n} from '../../utils/componentSizes';\nimport { Button } from '../Button';\nimport { Icon, type IconComponent } from '../Icon';\nimport { IconButton } from '../IconButton';\nimport { SpinnerIcon } from '../Spinner';\nimport { Text } from '../Text';\nimport { getAttachmentIcon } from './render-attachment-icon';\n\nexport interface AttachmentProps extends Omit<\n ComponentPropsWithRef<'div'>,\n 'children' | 'onClick'\n> {\n name: string;\n contentType?: string | null;\n icon?: IconComponent;\n /** Noninteractive thumbnail content. The consumer owns its source and lifecycle. */\n preview?: ReactNode;\n /** Secondary information, inline in compact rows and stacked in large rows. */\n metadata?: ReactNode;\n variant?: 'file' | 'thumbnail';\n /** File-row height: sm=24px, md=32px, lg=48px. Thumbnails use larger tiles. */\n size?: 'sm' | 'md' | 'lg';\n status?: 'ready' | 'loading' | 'uploading' | 'error';\n onOpen?: () => void;\n openLabel?: string;\n onRemove?: () => void;\n /** Sibling controls; use xs/sm/md buttons or smaller for sm/md/lg rows. */\n actions?: ReactNode;\n}\n\nconst SIZE_STYLES = {\n sm: {\n height: CONTROL_SIZES.sm.heightClassName,\n padding: 'px-space-1',\n media: VISUAL_ELEMENT_SIZES.xs.className,\n thumbnail: 'w-24',\n },\n md: {\n height: CONTROL_SIZES.md.heightClassName,\n padding: 'px-space-1',\n media: VISUAL_ELEMENT_SIZES.sm.className,\n thumbnail: 'w-32',\n },\n lg: {\n height: 'h-12',\n padding: 'px-space-2',\n media: 'size-9',\n thumbnail: 'w-40',\n },\n};\n\nexport const Attachment = forwardRef<HTMLDivElement, AttachmentProps>(\n function Attachment(\n {\n name,\n contentType,\n icon,\n preview,\n metadata,\n variant = 'file',\n size = 'md',\n status = 'ready',\n onOpen,\n openLabel = `View ${name}`,\n onRemove,\n actions,\n className,\n ...props\n },\n ref\n ) {\n const statusId = useId();\n const sizeStyles = SIZE_STYLES[size];\n const isThumbnail = variant === 'thumbnail';\n const hasMediaBackground = isThumbnail || size !== 'sm';\n const stackedText = isThumbnail || size === 'lg';\n const isBusy = status === 'loading' || status === 'uploading';\n const hasPreview = preview != null && status === 'ready';\n const description =\n status === 'uploading'\n ? 'Uploading…'\n : status === 'loading'\n ? 'Loading attachment…'\n : status === 'error'\n ? 'Upload failed.'\n : undefined;\n const removeButton = onRemove ? (\n <IconButton\n type=\"button\"\n icon={XIcon}\n iconWeight=\"regular\"\n iconClassName={\n isThumbnail\n ? size === 'sm'\n ? 'size-2.5'\n : 'size-3'\n : size === 'sm'\n ? 'size-3'\n : 'size-3.5'\n }\n label={`Remove ${name}`}\n color=\"secondary\"\n variant={isThumbnail ? 'normal' : 'plain'}\n round={isThumbnail}\n className={cn(\n isThumbnail && [\n 'absolute right-0 top-0',\n 'border-0 shadow-none hover:bg-elevated-hover',\n 'transition-opacity duration-fast motion-reduce:transition-none',\n '[@media(hover:hover)]:group-[:not(:is(:hover,:focus-within))]/attachment:pointer-events-none',\n '[@media(hover:hover)]:group-[:not(:is(:hover,:focus-within))]/attachment:opacity-0',\n ]\n )}\n size={size === 'sm' ? 'xxs' : size === 'md' ? 'xs' : 'sm'}\n onClick={(event) => {\n event.stopPropagation();\n onRemove();\n }}\n />\n ) : null;\n const media = hasPreview ? (\n preview\n ) : (\n <Icon\n aria-hidden\n icon={\n isBusy\n ? SpinnerIcon\n : status === 'error'\n ? WarningCircleIcon\n : (icon ?? getAttachmentIcon(contentType ?? null) ?? FileIcon)\n }\n size={size}\n weight=\"regular\"\n color={\n status === 'error'\n ? 'error'\n : hasMediaBackground\n ? 'neutral'\n : undefined\n }\n className={cn(\n 'rounded-xs',\n size === 'sm' && sizeStyles.media,\n !hasMediaBackground && 'bg-transparent dark:bg-transparent',\n status !== 'error' && [\n 'text-icon-secondary',\n hasMediaBackground && 'bg-surface-level-2',\n ]\n )}\n />\n );\n const content = (\n <span\n className={cn(\n 'flex min-w-0 flex-1 items-center',\n size === 'sm' && !isThumbnail ? 'gap-space-1' : 'gap-space-2',\n isThumbnail && 'w-full max-w-full flex-none flex-col items-stretch'\n )}\n >\n {hasPreview || isThumbnail ? (\n <span\n aria-hidden\n className={cn(\n 'flex shrink-0 items-center justify-center overflow-hidden rounded-xs [&>img]:size-full [&>img]:object-cover',\n isThumbnail && status === 'error'\n ? 'bg-error-subtle dark:bg-error'\n : hasMediaBackground && 'bg-surface-level-2',\n isThumbnail\n ? 'relative aspect-square w-full [&>img]:absolute [&>img]:inset-0'\n : sizeStyles.media\n )}\n >\n {media}\n </span>\n ) : (\n media\n )}\n <span\n className={cn(\n 'flex min-w-0 flex-1',\n stackedText ? 'flex-col gap-space-1' : 'items-baseline gap-space-2',\n isThumbnail && 'w-full max-w-full flex-none'\n )}\n >\n <Text\n as=\"span\"\n dir=\"rtl\"\n variant={size === 'sm' ? 'xs' : size === 'md' ? 'sm' : 'md'}\n className={cn(\n 'min-w-0 max-w-48 truncate text-left text-primary',\n isThumbnail && 'w-full max-w-full'\n )}\n title={name}\n >\n <bdi dir=\"ltr\">{name}</bdi>\n </Text>\n {metadata != null && !description && (\n <Text\n as=\"span\"\n variant=\"xs\"\n color=\"quaternary\"\n className={cn(\n 'min-w-0 max-w-48 truncate',\n isThumbnail && 'w-full max-w-full'\n )}\n title={typeof metadata === 'string' ? metadata : undefined}\n >\n {metadata}\n </Text>\n )}\n <span\n id={statusId}\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n className={cn(\n description ? 'min-w-0 max-w-full' : 'sr-only',\n description &&\n status === 'error' &&\n !stackedText &&\n 'max-w-[75%] shrink-0 grow'\n )}\n >\n {description && (\n <Text\n as=\"span\"\n variant=\"xs\"\n title={description}\n className={cn(\n 'block truncate text-tertiary',\n status === 'error' && 'text-error-secondary'\n )}\n >\n <span className=\"sr-only\">{name}: </span>\n {description}\n </Text>\n )}\n </span>\n </span>\n </span>\n );\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label={name}\n {...props}\n onClick={(event) => {\n // Extend the preview button to the card's padding and gaps only.\n if (onOpen && event.target === event.currentTarget) {\n event.stopPropagation();\n onOpen();\n }\n }}\n className={cn(\n 'box-border flex min-w-0 max-w-full shrink-0 items-center gap-space-2 rounded-xs border border-subtle bg-surface-level-1',\n isThumbnail\n ? [\n sizeStyles.thumbnail,\n 'group/attachment relative flex-col items-stretch',\n size === 'sm' ? 'p-space-1' : 'p-space-2',\n ]\n : [sizeStyles.height, sizeStyles.padding],\n status === 'error' && 'border-error',\n onOpen && 'cursor-pointer',\n className\n )}\n >\n {onOpen ? (\n <Button\n type=\"button\"\n color=\"secondary\"\n variant=\"plain\"\n aria-label={openLabel}\n aria-describedby={description ? statusId : undefined}\n className={cn(\n 'min-w-0 flex-1 justify-start p-0 text-left',\n isThumbnail\n ? 'h-auto w-full max-w-full flex-none whitespace-normal'\n : 'h-full'\n )}\n onClick={(event) => {\n event.stopPropagation();\n onOpen();\n }}\n >\n {content}\n </Button>\n ) : (\n content\n )}\n {isThumbnail && removeButton}\n {(actions != null || (!isThumbnail && onRemove)) && (\n <fieldset\n aria-label={`Actions for ${name}`}\n className={cn(\n 'm-0 flex min-w-0 max-w-full shrink-0 items-center justify-end gap-space-1 border-0 p-0',\n isThumbnail && 'flex-wrap'\n )}\n onClick={(event) => event.stopPropagation()}\n >\n {actions}\n {!isThumbnail && removeButton}\n </fieldset>\n )}\n </div>\n );\n }\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Attachment.js","names":[],"sources":["../../../src/components/Attachment/Attachment.tsx"],"sourcesContent":["import type { ComponentPropsWithRef, ReactNode } from 'react';\nimport { forwardRef, useId } from 'react';\n\nimport { FileIcon } from '@phosphor-icons/react/dist/ssr/File';\nimport { WarningCircleIcon } from '@phosphor-icons/react/dist/ssr/WarningCircle';\nimport { XIcon } from '@phosphor-icons/react/dist/ssr/X';\n\nimport { cn } from '../../utils/cn';\nimport {\n CONTROL_SIZES,\n VISUAL_ELEMENT_SIZES,\n} from '../../utils/componentSizes';\nimport { Button } from '../Button';\nimport { Icon, type IconComponent } from '../Icon';\nimport { IconButton } from '../IconButton';\nimport { SpinnerIcon } from '../Spinner';\nimport { Text } from '../Text';\nimport { getAttachmentIcon } from './render-attachment-icon';\n\nexport interface AttachmentProps extends Omit<\n ComponentPropsWithRef<'div'>,\n 'children' | 'onClick'\n> {\n name: string;\n contentType?: string | null;\n icon?: IconComponent;\n /** Noninteractive thumbnail content. The consumer owns its source and lifecycle. */\n preview?: ReactNode;\n /** Secondary information, inline in compact rows and stacked in large rows. */\n metadata?: ReactNode;\n variant?: 'file' | 'thumbnail';\n /** File-row height: sm=24px, md=32px, lg=48px. Thumbnails use larger tiles. */\n size?: 'sm' | 'md' | 'lg';\n status?: 'ready' | 'loading' | 'uploading' | 'error';\n /** Disables opening, removal, and custom actions. */\n disabled?: boolean;\n onOpen?: () => void;\n openLabel?: string;\n onRemove?: () => void;\n /** Sibling controls; use xs/sm/md buttons or smaller for sm/md/lg rows. */\n actions?: ReactNode;\n}\n\nconst SIZE_STYLES = {\n sm: {\n height: CONTROL_SIZES.sm.heightClassName,\n padding: 'px-space-1',\n media: VISUAL_ELEMENT_SIZES.xs.className,\n thumbnail: 'w-24',\n },\n md: {\n height: CONTROL_SIZES.md.heightClassName,\n padding: 'px-space-1',\n media: VISUAL_ELEMENT_SIZES.sm.className,\n thumbnail: 'w-32',\n },\n lg: {\n height: 'h-12',\n padding: 'px-space-2',\n media: 'size-9',\n thumbnail: 'w-40',\n },\n};\n\nexport const Attachment = forwardRef<HTMLDivElement, AttachmentProps>(\n function Attachment(\n {\n name,\n contentType,\n icon,\n preview,\n metadata,\n variant = 'file',\n size = 'md',\n status = 'ready',\n disabled = false,\n onOpen,\n openLabel = `View ${name}`,\n onRemove,\n actions,\n className,\n ...props\n },\n ref\n ) {\n const statusId = useId();\n const sizeStyles = SIZE_STYLES[size];\n const isThumbnail = variant === 'thumbnail';\n const hasMediaBackground = isThumbnail || size !== 'sm';\n const stackedText = isThumbnail || size === 'lg';\n const isBusy = status === 'loading' || status === 'uploading';\n const hasPreview = preview != null && status === 'ready';\n const description =\n status === 'uploading'\n ? 'Uploading…'\n : status === 'loading'\n ? 'Loading attachment…'\n : status === 'error'\n ? 'Upload failed.'\n : undefined;\n const removeButton = onRemove ? (\n <IconButton\n type=\"button\"\n icon={XIcon}\n iconWeight=\"regular\"\n iconClassName={\n isThumbnail\n ? size === 'sm'\n ? 'size-2.5'\n : 'size-3'\n : size === 'sm'\n ? 'size-3'\n : 'size-3.5'\n }\n label={`Remove ${name}`}\n disabled={disabled}\n color=\"secondary\"\n variant={isThumbnail ? 'normal' : 'plain'}\n round={isThumbnail}\n className={cn(\n isThumbnail && [\n 'absolute right-0 top-0',\n 'border-0 shadow-none hover:bg-elevated-hover',\n 'transition-opacity duration-fast motion-reduce:transition-none',\n '[@media(hover:hover)]:group-[:not(:is(:hover,:focus-within))]/attachment:pointer-events-none',\n '[@media(hover:hover)]:group-[:not(:is(:hover,:focus-within))]/attachment:opacity-0',\n ]\n )}\n size={size === 'sm' ? 'xxs' : size === 'md' ? 'xs' : 'sm'}\n onClick={(event) => {\n event.stopPropagation();\n if (!disabled) onRemove();\n }}\n />\n ) : null;\n const media = hasPreview ? (\n preview\n ) : (\n <Icon\n aria-hidden\n icon={\n isBusy\n ? SpinnerIcon\n : status === 'error'\n ? WarningCircleIcon\n : (icon ?? getAttachmentIcon(contentType ?? null) ?? FileIcon)\n }\n size={size}\n weight=\"regular\"\n color={\n status === 'error'\n ? 'error'\n : hasMediaBackground\n ? 'neutral'\n : undefined\n }\n className={cn(\n 'rounded-xs',\n size === 'sm' && sizeStyles.media,\n !hasMediaBackground && 'bg-transparent dark:bg-transparent',\n status !== 'error' && [\n 'text-icon-secondary',\n hasMediaBackground && 'bg-surface-level-2',\n ]\n )}\n />\n );\n const content = (\n <span\n className={cn(\n 'flex min-w-0 flex-1 items-center',\n size === 'sm' && !isThumbnail ? 'gap-space-1' : 'gap-space-2',\n isThumbnail && 'w-full max-w-full flex-none flex-col items-stretch'\n )}\n >\n {hasPreview || isThumbnail ? (\n <span\n aria-hidden\n className={cn(\n 'flex shrink-0 items-center justify-center overflow-hidden rounded-xs [&>img]:size-full [&>img]:object-cover',\n isThumbnail && status === 'error'\n ? 'bg-error-subtle dark:bg-error'\n : hasMediaBackground && 'bg-surface-level-2',\n isThumbnail\n ? 'relative aspect-square w-full [&>img]:absolute [&>img]:inset-0'\n : sizeStyles.media\n )}\n >\n {media}\n </span>\n ) : (\n media\n )}\n <span\n className={cn(\n 'flex min-w-0 flex-1',\n stackedText ? 'flex-col gap-space-1' : 'items-baseline gap-space-2',\n isThumbnail && 'w-full max-w-full flex-none'\n )}\n >\n <Text\n as=\"span\"\n dir=\"rtl\"\n variant={size === 'sm' ? 'xs' : size === 'md' ? 'sm' : 'md'}\n className={cn(\n 'min-w-0 max-w-48 truncate text-left text-primary',\n isThumbnail && 'w-full max-w-full'\n )}\n title={name}\n >\n <bdi dir=\"ltr\">{name}</bdi>\n </Text>\n {metadata != null && !description && (\n <Text\n as=\"span\"\n variant=\"xs\"\n color=\"quaternary\"\n className={cn(\n 'min-w-0 max-w-48 truncate',\n isThumbnail && 'w-full max-w-full'\n )}\n title={typeof metadata === 'string' ? metadata : undefined}\n >\n {metadata}\n </Text>\n )}\n <span\n id={statusId}\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n className={cn(\n description ? 'min-w-0 max-w-full' : 'sr-only',\n description &&\n status === 'error' &&\n !stackedText &&\n 'max-w-[75%] shrink-0 grow'\n )}\n >\n {description && (\n <Text\n as=\"span\"\n variant=\"xs\"\n title={description}\n className={cn(\n 'block truncate text-tertiary',\n status === 'error' && 'text-error-secondary'\n )}\n >\n <span className=\"sr-only\">{name}: </span>\n {description}\n </Text>\n )}\n </span>\n </span>\n </span>\n );\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label={name}\n {...props}\n aria-disabled={disabled || undefined}\n onClick={(event) => {\n // Extend the preview button to the card's padding and gaps only.\n if (!disabled && onOpen && event.target === event.currentTarget) {\n event.stopPropagation();\n onOpen();\n }\n }}\n className={cn(\n 'box-border flex min-w-0 max-w-full shrink-0 items-center gap-space-2 rounded-xs border border-subtle bg-surface-level-1',\n isThumbnail\n ? [\n sizeStyles.thumbnail,\n 'group/attachment relative flex-col items-stretch',\n size === 'sm' ? 'p-space-1' : 'p-space-2',\n ]\n : [sizeStyles.height, sizeStyles.padding],\n status === 'error' && 'border-error',\n onOpen && !disabled && 'cursor-pointer',\n disabled && 'cursor-not-allowed opacity-50',\n className\n )}\n >\n {onOpen ? (\n <Button\n type=\"button\"\n color=\"secondary\"\n variant=\"plain\"\n aria-label={openLabel}\n disabled={disabled}\n aria-describedby={description ? statusId : undefined}\n className={cn(\n 'min-w-0 flex-1 justify-start p-0 text-left',\n isThumbnail\n ? 'h-auto w-full max-w-full flex-none whitespace-normal'\n : 'h-full'\n )}\n onClick={(event) => {\n event.stopPropagation();\n if (!disabled) onOpen();\n }}\n >\n {content}\n </Button>\n ) : (\n content\n )}\n {isThumbnail && removeButton}\n {(actions != null || (!isThumbnail && onRemove)) && (\n <fieldset\n disabled={disabled}\n inert={disabled || undefined}\n aria-label={`Actions for ${name}`}\n className={cn(\n 'm-0 flex min-w-0 max-w-full shrink-0 items-center justify-end gap-space-1 border-0 p-0',\n isThumbnail && 'flex-wrap'\n )}\n onClick={(event) => event.stopPropagation()}\n >\n {actions}\n {!isThumbnail && removeButton}\n </fieldset>\n )}\n </div>\n );\n }\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ComponentPropsWithoutRef } from 'react';
|
|
2
|
+
declare const BLOCK_SIZES: {
|
|
3
|
+
sm: number;
|
|
4
|
+
md: number;
|
|
5
|
+
lg: number;
|
|
6
|
+
};
|
|
7
|
+
declare const BLOCK_COLORS: {
|
|
8
|
+
yellow: string;
|
|
9
|
+
green: string;
|
|
10
|
+
red: string;
|
|
11
|
+
blue: string;
|
|
12
|
+
};
|
|
13
|
+
export type HeatmapBlockSize = keyof typeof BLOCK_SIZES;
|
|
14
|
+
export type HeatmapBlockColor = keyof typeof BLOCK_COLORS;
|
|
15
|
+
export interface HeatmapBlockProps extends Pick<ComponentPropsWithoutRef<'span'>, 'tabIndex' | 'onFocus' | 'onKeyDown'> {
|
|
16
|
+
opacity: number;
|
|
17
|
+
size?: HeatmapBlockSize;
|
|
18
|
+
color?: HeatmapBlockColor;
|
|
19
|
+
label?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const HeatmapBlock: import("react").ForwardRefExoticComponent<HeatmapBlockProps & import("react").RefAttributes<HTMLSpanElement>>;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Tooltip } from "../Tooltip/Tooltip.js";
|
|
2
|
+
import { clamp } from "../../utils/clamp.js";
|
|
3
|
+
import { getCategoricalFillChartColor } from "../../utils/chartColors.js";
|
|
4
|
+
import { forwardRef } from "react";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
//#region packages/components/src/components/HeatmapChart/HeatmapBlock.tsx
|
|
7
|
+
var BLOCK_SIZES = {
|
|
8
|
+
sm: 12,
|
|
9
|
+
md: 16,
|
|
10
|
+
lg: 20
|
|
11
|
+
};
|
|
12
|
+
var BLOCK_COLORS = {
|
|
13
|
+
yellow: getCategoricalFillChartColor(4),
|
|
14
|
+
green: getCategoricalFillChartColor(2),
|
|
15
|
+
red: getCategoricalFillChartColor(7),
|
|
16
|
+
blue: getCategoricalFillChartColor(6)
|
|
17
|
+
};
|
|
18
|
+
var HeatmapBlock = forwardRef(function HeatmapBlock({ opacity, size = "md", color = "green", label, tabIndex = 0, onFocus, onKeyDown }, ref) {
|
|
19
|
+
const value = Number.isFinite(opacity) ? clamp(opacity, 0, 100) : 0;
|
|
20
|
+
const dimension = BLOCK_SIZES[size];
|
|
21
|
+
const accessibleLabel = label ?? `${value}%`;
|
|
22
|
+
return /* @__PURE__ */ jsx(Tooltip, {
|
|
23
|
+
title: accessibleLabel,
|
|
24
|
+
disableHoverableContent: false,
|
|
25
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
26
|
+
ref,
|
|
27
|
+
role: "img",
|
|
28
|
+
"aria-label": accessibleLabel,
|
|
29
|
+
tabIndex,
|
|
30
|
+
onFocus,
|
|
31
|
+
onKeyDown,
|
|
32
|
+
className: "inline-flex shrink-0 text-primary focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2",
|
|
33
|
+
style: {
|
|
34
|
+
width: dimension,
|
|
35
|
+
height: dimension
|
|
36
|
+
},
|
|
37
|
+
children: /* @__PURE__ */ jsx("svg", {
|
|
38
|
+
width: dimension,
|
|
39
|
+
height: dimension,
|
|
40
|
+
"aria-hidden": "true",
|
|
41
|
+
children: /* @__PURE__ */ jsx("rect", {
|
|
42
|
+
width: dimension,
|
|
43
|
+
height: dimension,
|
|
44
|
+
fill: BLOCK_COLORS[color],
|
|
45
|
+
fillOpacity: value / 100
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
//#endregion
|
|
52
|
+
export { HeatmapBlock };
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=HeatmapBlock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HeatmapBlock.js","names":[],"sources":["../../../src/components/HeatmapChart/HeatmapBlock.tsx"],"sourcesContent":["import { forwardRef, type ComponentPropsWithoutRef } from 'react';\n\nimport { getCategoricalFillChartColor } from '../../utils/chartColors';\nimport { clamp } from '../../utils/clamp';\nimport { Tooltip } from '../Tooltip';\n\nconst BLOCK_SIZES = { sm: 12, md: 16, lg: 20 };\nconst BLOCK_COLORS = {\n yellow: getCategoricalFillChartColor(4),\n green: getCategoricalFillChartColor(2),\n red: getCategoricalFillChartColor(7),\n blue: getCategoricalFillChartColor(6),\n};\n\nexport type HeatmapBlockSize = keyof typeof BLOCK_SIZES;\nexport type HeatmapBlockColor = keyof typeof BLOCK_COLORS;\n\nexport interface HeatmapBlockProps extends Pick<\n ComponentPropsWithoutRef<'span'>,\n 'tabIndex' | 'onFocus' | 'onKeyDown'\n> {\n opacity: number;\n size?: HeatmapBlockSize;\n color?: HeatmapBlockColor;\n label?: string;\n}\n\nexport const HeatmapBlock = forwardRef<HTMLSpanElement, HeatmapBlockProps>(\n function HeatmapBlock(\n {\n opacity,\n size = 'md',\n color = 'green',\n label,\n tabIndex = 0,\n onFocus,\n onKeyDown,\n },\n ref\n ) {\n const value = Number.isFinite(opacity) ? clamp(opacity, 0, 100) : 0;\n const dimension = BLOCK_SIZES[size];\n const accessibleLabel = label ?? `${value}%`;\n\n return (\n <Tooltip title={accessibleLabel} disableHoverableContent={false}>\n <span\n ref={ref}\n role=\"img\"\n aria-label={accessibleLabel}\n tabIndex={tabIndex}\n onFocus={onFocus}\n onKeyDown={onKeyDown}\n className=\"inline-flex shrink-0 text-primary focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2\"\n style={{ width: dimension, height: dimension }}\n >\n <svg width={dimension} height={dimension} aria-hidden=\"true\">\n <rect\n width={dimension}\n height={dimension}\n fill={BLOCK_COLORS[color]}\n fillOpacity={value / 100}\n />\n </svg>\n </span>\n </Tooltip>\n );\n }\n);\n"],"mappings":";;;;;;AAMA,IAAM,cAAc;CAAE,IAAI;CAAI,IAAI;CAAI,IAAI;AAAG;AAC7C,IAAM,eAAe;CACnB,QAAQ,6BAA6B,CAAC;CACtC,OAAO,6BAA6B,CAAC;CACrC,KAAK,6BAA6B,CAAC;CACnC,MAAM,6BAA6B,CAAC;AACtC;AAeA,IAAa,eAAe,WAC1B,SAAS,aACP,EACE,SACA,OAAO,MACP,QAAQ,SACR,OACA,WAAW,GACX,SACA,aAEF,KACA;CACA,MAAM,QAAQ,OAAO,SAAS,OAAO,IAAI,MAAM,SAAS,GAAG,GAAG,IAAI;CAClE,MAAM,YAAY,YAAY;CAC9B,MAAM,kBAAkB,SAAS,GAAG,MAAM;CAE1C,OACE,oBAAC,SAAD;EAAS,OAAO;EAAiB,yBAAyB;EACxD,UAAA,oBAAC,QAAD;GACO;GACL,MAAK;GACL,cAAY;GACF;GACD;GACE;GACX,WAAU;GACV,OAAO;IAAE,OAAO;IAAW,QAAQ;GAAU;GAE7C,UAAA,oBAAC,OAAD;IAAK,OAAO;IAAW,QAAQ;IAAW,eAAY;IACpD,UAAA,oBAAC,QAAD;KACE,OAAO;KACP,QAAQ;KACR,MAAM,aAAa;KACnB,aAAa,QAAQ;IACtB,CAAA;GACE,CAAA;EACD,CAAA;CACC,CAAA;AAEb,CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type HTMLAttributes } from 'react';
|
|
2
|
+
import { type HeatmapBlockProps } from './HeatmapBlock.js';
|
|
3
|
+
export interface HeatmapChartProps extends HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
data: readonly (readonly number[])[];
|
|
5
|
+
size?: HeatmapBlockProps['size'];
|
|
6
|
+
color?: HeatmapBlockProps['color'];
|
|
7
|
+
getBlockLabel?: (value: number, row: number, column: number) => string;
|
|
8
|
+
}
|
|
9
|
+
export declare function HeatmapChart({ data, size, color, getBlockLabel, className, 'aria-label': ariaLabel, ...props }: HeatmapChartProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { HeatmapBlock } from "./HeatmapBlock.js";
|
|
3
|
+
import { useRef, useState } from "react";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region packages/components/src/components/HeatmapChart/HeatmapChart.tsx
|
|
6
|
+
function HeatmapChart({ data, size = "md", color = "green", getBlockLabel = (value, row, column) => `Row ${row + 1}, column ${column + 1}: ${value}%`, className, "aria-label": ariaLabel = "Heatmap", ...props }) {
|
|
7
|
+
const [focusedIndex, setFocusedIndex] = useState(0);
|
|
8
|
+
const cellRefs = useRef([]);
|
|
9
|
+
let cellCount = 0;
|
|
10
|
+
const rowOffsets = data.map((row) => {
|
|
11
|
+
const offset = cellCount;
|
|
12
|
+
cellCount += row.length;
|
|
13
|
+
return offset;
|
|
14
|
+
});
|
|
15
|
+
const activeIndex = Math.min(focusedIndex, Math.max(0, cellCount - 1));
|
|
16
|
+
function moveFocus(event, row, column) {
|
|
17
|
+
const offset = rowOffsets[row];
|
|
18
|
+
const length = data[row].length;
|
|
19
|
+
let nextIndex = offset + column;
|
|
20
|
+
switch (event.key) {
|
|
21
|
+
case "ArrowLeft":
|
|
22
|
+
nextIndex = offset + Math.max(0, column - 1);
|
|
23
|
+
break;
|
|
24
|
+
case "ArrowRight":
|
|
25
|
+
nextIndex = offset + Math.min(length - 1, column + 1);
|
|
26
|
+
break;
|
|
27
|
+
case "ArrowUp":
|
|
28
|
+
case "ArrowDown": {
|
|
29
|
+
const direction = event.key === "ArrowUp" ? -1 : 1;
|
|
30
|
+
let nextRow = row + direction;
|
|
31
|
+
while (nextRow >= 0 && nextRow < data.length) {
|
|
32
|
+
if (data[nextRow].length > 0) {
|
|
33
|
+
nextIndex = rowOffsets[nextRow] + Math.min(column, data[nextRow].length - 1);
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
nextRow += direction;
|
|
37
|
+
}
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
case "Home":
|
|
41
|
+
nextIndex = event.ctrlKey ? 0 : offset;
|
|
42
|
+
break;
|
|
43
|
+
case "End":
|
|
44
|
+
nextIndex = event.ctrlKey ? cellCount - 1 : offset + length - 1;
|
|
45
|
+
break;
|
|
46
|
+
default: return;
|
|
47
|
+
}
|
|
48
|
+
event.preventDefault();
|
|
49
|
+
cellRefs.current[nextIndex]?.focus();
|
|
50
|
+
}
|
|
51
|
+
return /* @__PURE__ */ jsx("div", {
|
|
52
|
+
...props,
|
|
53
|
+
role: "group",
|
|
54
|
+
"aria-label": ariaLabel,
|
|
55
|
+
className: cn("w-full min-w-0 overflow-x-auto", className),
|
|
56
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
57
|
+
className: "flex w-max flex-col gap-space-1 p-space-1",
|
|
58
|
+
children: data.map((row, rowIndex) => /* @__PURE__ */ jsx("div", {
|
|
59
|
+
className: "flex gap-space-1",
|
|
60
|
+
children: row.map((value, columnIndex) => /* @__PURE__ */ jsx(HeatmapBlock, {
|
|
61
|
+
ref: (element) => {
|
|
62
|
+
cellRefs.current[rowOffsets[rowIndex] + columnIndex] = element;
|
|
63
|
+
},
|
|
64
|
+
tabIndex: rowOffsets[rowIndex] + columnIndex === activeIndex ? 0 : -1,
|
|
65
|
+
onFocus: () => setFocusedIndex(rowOffsets[rowIndex] + columnIndex),
|
|
66
|
+
onKeyDown: (event) => moveFocus(event, rowIndex, columnIndex),
|
|
67
|
+
opacity: value,
|
|
68
|
+
size,
|
|
69
|
+
color,
|
|
70
|
+
label: getBlockLabel(value, rowIndex, columnIndex)
|
|
71
|
+
}, columnIndex))
|
|
72
|
+
}, rowIndex))
|
|
73
|
+
})
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { HeatmapChart };
|
|
78
|
+
|
|
79
|
+
//# sourceMappingURL=HeatmapChart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HeatmapChart.js","names":[],"sources":["../../../src/components/HeatmapChart/HeatmapChart.tsx"],"sourcesContent":["import {\n useRef,\n useState,\n type HTMLAttributes,\n type KeyboardEvent,\n} from 'react';\n\nimport { cn } from '../../utils/cn';\nimport { HeatmapBlock, type HeatmapBlockProps } from './HeatmapBlock';\n\nexport interface HeatmapChartProps extends HTMLAttributes<HTMLDivElement> {\n data: readonly (readonly number[])[];\n size?: HeatmapBlockProps['size'];\n color?: HeatmapBlockProps['color'];\n getBlockLabel?: (value: number, row: number, column: number) => string;\n}\n\nexport function HeatmapChart({\n data,\n size = 'md',\n color = 'green',\n getBlockLabel = (value, row, column) =>\n `Row ${row + 1}, column ${column + 1}: ${value}%`,\n className,\n 'aria-label': ariaLabel = 'Heatmap',\n ...props\n}: HeatmapChartProps) {\n const [focusedIndex, setFocusedIndex] = useState(0);\n const cellRefs = useRef<(HTMLSpanElement | null)[]>([]);\n let cellCount = 0;\n const rowOffsets = data.map((row) => {\n const offset = cellCount;\n cellCount += row.length;\n return offset;\n });\n const activeIndex = Math.min(focusedIndex, Math.max(0, cellCount - 1));\n\n function moveFocus(\n event: KeyboardEvent<HTMLSpanElement>,\n row: number,\n column: number\n ) {\n const offset = rowOffsets[row];\n const length = data[row].length;\n let nextIndex = offset + column;\n switch (event.key) {\n case 'ArrowLeft':\n nextIndex = offset + Math.max(0, column - 1);\n break;\n case 'ArrowRight':\n nextIndex = offset + Math.min(length - 1, column + 1);\n break;\n case 'ArrowUp':\n case 'ArrowDown': {\n const direction = event.key === 'ArrowUp' ? -1 : 1;\n let nextRow = row + direction;\n while (nextRow >= 0 && nextRow < data.length) {\n if (data[nextRow].length > 0) {\n nextIndex =\n rowOffsets[nextRow] + Math.min(column, data[nextRow].length - 1);\n break;\n }\n nextRow += direction;\n }\n break;\n }\n case 'Home':\n nextIndex = event.ctrlKey ? 0 : offset;\n break;\n case 'End':\n nextIndex = event.ctrlKey ? cellCount - 1 : offset + length - 1;\n break;\n default:\n return;\n }\n event.preventDefault();\n cellRefs.current[nextIndex]?.focus();\n }\n\n return (\n <div\n {...props}\n role=\"group\"\n aria-label={ariaLabel}\n className={cn('w-full min-w-0 overflow-x-auto', className)}\n >\n <div className=\"flex w-max flex-col gap-space-1 p-space-1\">\n {data.map((row, rowIndex) => (\n <div key={rowIndex} className=\"flex gap-space-1\">\n {row.map((value, columnIndex) => (\n <HeatmapBlock\n key={columnIndex}\n ref={(element) => {\n cellRefs.current[rowOffsets[rowIndex] + columnIndex] =\n element;\n }}\n tabIndex={\n rowOffsets[rowIndex] + columnIndex === activeIndex ? 0 : -1\n }\n onFocus={() =>\n setFocusedIndex(rowOffsets[rowIndex] + columnIndex)\n }\n onKeyDown={(event) => moveFocus(event, rowIndex, columnIndex)}\n opacity={value}\n size={size}\n color={color}\n label={getBlockLabel(value, rowIndex, columnIndex)}\n />\n ))}\n </div>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAiBA,SAAgB,aAAa,EAC3B,MACA,OAAO,MACP,QAAQ,SACR,iBAAiB,OAAO,KAAK,WAC3B,OAAO,MAAM,EAAE,WAAW,SAAS,EAAE,IAAI,MAAM,IACjD,WACA,cAAc,YAAY,WAC1B,GAAG,SACiB;CACpB,MAAM,CAAC,cAAc,mBAAmB,SAAS,CAAC;CAClD,MAAM,WAAW,OAAmC,CAAC,CAAC;CACtD,IAAI,YAAY;CAChB,MAAM,aAAa,KAAK,KAAK,QAAQ;EACnC,MAAM,SAAS;EACf,aAAa,IAAI;EACjB,OAAO;CACT,CAAC;CACD,MAAM,cAAc,KAAK,IAAI,cAAc,KAAK,IAAI,GAAG,YAAY,CAAC,CAAC;CAErE,SAAS,UACP,OACA,KACA,QACA;EACA,MAAM,SAAS,WAAW;EAC1B,MAAM,SAAS,KAAK,IAAI,CAAC;EACzB,IAAI,YAAY,SAAS;EACzB,QAAQ,MAAM,KAAd;GACE,KAAK;IACH,YAAY,SAAS,KAAK,IAAI,GAAG,SAAS,CAAC;IAC3C;GACF,KAAK;IACH,YAAY,SAAS,KAAK,IAAI,SAAS,GAAG,SAAS,CAAC;IACpD;GACF,KAAK;GACL,KAAK,aAAa;IAChB,MAAM,YAAY,MAAM,QAAQ,YAAY,KAAK;IACjD,IAAI,UAAU,MAAM;IACpB,OAAO,WAAW,KAAK,UAAU,KAAK,QAAQ;KAC5C,IAAI,KAAK,QAAQ,CAAC,SAAS,GAAG;MAC5B,YACE,WAAW,WAAW,KAAK,IAAI,QAAQ,KAAK,QAAQ,CAAC,SAAS,CAAC;MACjE;KACF;KACA,WAAW;IACb;IACA;GACF;GACA,KAAK;IACH,YAAY,MAAM,UAAU,IAAI;IAChC;GACF,KAAK;IACH,YAAY,MAAM,UAAU,YAAY,IAAI,SAAS,SAAS;IAC9D;GACF,SACE;EACJ;EACA,MAAM,eAAe;EACrB,SAAS,QAAQ,UAAU,EAAE,MAAM;CACrC;CAEA,OACE,oBAAC,OAAD;EACE,GAAI;EACJ,MAAK;EACL,cAAY;EACZ,WAAW,GAAG,kCAAkC,SAAS;EAEzD,UAAA,oBAAC,OAAD;GAAK,WAAU;GACZ,UAAA,KAAK,KAAK,KAAK,aACd,oBAAC,OAAD;IAAoB,WAAU;IAC3B,UAAA,IAAI,KAAK,OAAO,gBACf,oBAAC,cAAD;KAEE,MAAM,YAAY;MAChB,SAAS,QAAQ,WAAW,YAAY,eACtC;KACJ;KACA,UACE,WAAW,YAAY,gBAAgB,cAAc,IAAI;KAE3D,eACE,gBAAgB,WAAW,YAAY,WAAW;KAEpD,YAAY,UAAU,UAAU,OAAO,UAAU,WAAW;KAC5D,SAAS;KACH;KACC;KACP,OAAO,cAAc,OAAO,UAAU,WAAW;IAClD,GAhBM,WAgBN,CACF;GACE,GArBK,QAqBL,CACN;EACE,CAAA;CACF,CAAA;AAET"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { ComponentPropsWithRef } from 'react';
|
|
2
2
|
type ThinkingStateVariant = 'drive' | 'dots' | 'orbit';
|
|
3
3
|
declare const SIZES: {
|
|
4
|
+
xs: "size-3";
|
|
4
5
|
sm: "size-4";
|
|
5
6
|
md: "size-5";
|
|
6
7
|
lg: "size-6";
|
|
7
8
|
};
|
|
8
9
|
interface LoadingIndicatorProps extends Omit<ComponentPropsWithRef<'span'>, 'children'> {
|
|
9
10
|
variant?: ThinkingStateVariant;
|
|
10
|
-
/** Indicator outer size: sm=16px, md=20px, lg=24px. */
|
|
11
|
+
/** Indicator outer size: xs=12px, sm=16px, md=20px, lg=24px. */
|
|
11
12
|
size?: keyof typeof SIZES;
|
|
12
13
|
tone?: 'neutral' | 'brand';
|
|
13
14
|
/** Reduced-motion preferences always take precedence. */
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as LoadingIndicator } from "../../_chunks/LoadingIndicator-
|
|
1
|
+
import { t as LoadingIndicator } from "../../_chunks/LoadingIndicator-DujSAzqj.js";
|
|
2
2
|
export { LoadingIndicator };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "../../utils/cn.js";
|
|
2
2
|
import { Text } from "../Text/Text.js";
|
|
3
|
-
import { n as ThinkingState_module_default, t as LoadingIndicator } from "../../_chunks/LoadingIndicator-
|
|
3
|
+
import { n as ThinkingState_module_default, t as LoadingIndicator } from "../../_chunks/LoadingIndicator-DujSAzqj.js";
|
|
4
4
|
import { ThinkingStateTimer } from "./ThinkingStateTimer.js";
|
|
5
5
|
import { forwardRef } from "react";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -21,12 +21,12 @@ var ThinkingState = forwardRef(function ThinkingState({ label = "Thinking", vari
|
|
|
21
21
|
className: "inline-flex min-w-0 items-baseline gap-space-2",
|
|
22
22
|
children: [/* @__PURE__ */ jsx(Text, {
|
|
23
23
|
as: "span",
|
|
24
|
-
variant: size === "sm" ? "sm" : "md",
|
|
25
|
-
weight: "medium",
|
|
24
|
+
variant: size === "xs" ? "xs" : size === "sm" ? "sm" : "md",
|
|
25
|
+
weight: size === "xs" ? "normal" : "medium",
|
|
26
26
|
role: "status",
|
|
27
27
|
"aria-live": "polite",
|
|
28
28
|
"aria-atomic": "true",
|
|
29
|
-
className: cn("min-w-0 break-words leading-4", motion === "full" ? ThinkingState_module_default.shimmer : "text-secondary", motion === "full" && tone === "brand" && ThinkingState_module_default.brandShimmer, speed === "slow" && ThinkingState_module_default.slow),
|
|
29
|
+
className: cn("min-w-0 break-words", size === "xs" ? "leading-snug tracking-normal" : "leading-4", motion === "full" ? ThinkingState_module_default.shimmer : "text-secondary", motion === "full" && tone === "brand" && ThinkingState_module_default.brandShimmer, speed === "slow" && ThinkingState_module_default.slow),
|
|
30
30
|
children: label
|
|
31
31
|
}), /* @__PURE__ */ jsx(ThinkingStateTimer, {
|
|
32
32
|
visible: showElapsed,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThinkingState.js","names":[],"sources":["../../../src/components/ThinkingState/ThinkingState.tsx"],"sourcesContent":["import { forwardRef } from 'react';\nimport type { ComponentPropsWithRef, ReactNode } from 'react';\n\nimport { cn } from '../../utils/cn';\nimport { Text } from '../Text';\nimport {\n LoadingIndicator,\n type LoadingIndicatorProps,\n} from './LoadingIndicator';\nimport { ThinkingStateTimer } from './ThinkingStateTimer';\n\nimport styles from './ThinkingState.module.css';\n\ninterface ThinkingStateProps extends Omit<\n ComponentPropsWithRef<'div'>,\n 'children'\n> {\n /** Short, noninteractive description of the work in progress. */\n label?: ReactNode;\n variant?: LoadingIndicatorProps['variant'];\n size?: LoadingIndicatorProps['size'];\n tone?: LoadingIndicatorProps['tone'];\n /** Full animates the grid and label; subtle animates only the grid; none is static. */\n motion?: 'full' | 'subtle' | 'none';\n speed?: LoadingIndicatorProps['speed'];\n /** Opt in to elapsed time for longer waits. Hidden by default. */\n showElapsed?: boolean;\n /** Supply elapsed milliseconds from the operation to disable the internal clock. */\n elapsedMs?: number;\n /** Seconds updates once per second; tenths preserves the finer-grained source timer. */\n timerPrecision?: 'seconds' | 'tenths';\n}\n\n/** Inline status for active AI work. Remount with a new key for a new operation. */\nconst ThinkingState = forwardRef<HTMLDivElement, ThinkingStateProps>(\n function ThinkingState(\n {\n label = 'Thinking',\n variant = 'drive',\n size = 'sm',\n tone = 'neutral',\n motion = 'full',\n speed = 'normal',\n showElapsed = false,\n elapsedMs,\n timerPrecision = 'seconds',\n className,\n ...props\n },\n ref\n ) {\n return (\n <div\n {...props}\n ref={ref}\n className={cn(\n 'inline-flex max-w-full items-center gap-space-2 align-middle',\n className\n )}\n >\n <LoadingIndicator\n variant={variant}\n size={size}\n tone={tone}\n animated={motion !== 'none'}\n speed={speed}\n />\n <span className=\"inline-flex min-w-0 items-baseline gap-space-2\">\n <Text\n as=\"span\"\n variant={size === 'sm' ? 'sm' : 'md'}\n weight
|
|
1
|
+
{"version":3,"file":"ThinkingState.js","names":[],"sources":["../../../src/components/ThinkingState/ThinkingState.tsx"],"sourcesContent":["import { forwardRef } from 'react';\nimport type { ComponentPropsWithRef, ReactNode } from 'react';\n\nimport { cn } from '../../utils/cn';\nimport { Text } from '../Text';\nimport {\n LoadingIndicator,\n type LoadingIndicatorProps,\n} from './LoadingIndicator';\nimport { ThinkingStateTimer } from './ThinkingStateTimer';\n\nimport styles from './ThinkingState.module.css';\n\ninterface ThinkingStateProps extends Omit<\n ComponentPropsWithRef<'div'>,\n 'children'\n> {\n /** Short, noninteractive description of the work in progress. */\n label?: ReactNode;\n variant?: LoadingIndicatorProps['variant'];\n size?: LoadingIndicatorProps['size'];\n tone?: LoadingIndicatorProps['tone'];\n /** Full animates the grid and label; subtle animates only the grid; none is static. */\n motion?: 'full' | 'subtle' | 'none';\n speed?: LoadingIndicatorProps['speed'];\n /** Opt in to elapsed time for longer waits. Hidden by default. */\n showElapsed?: boolean;\n /** Supply elapsed milliseconds from the operation to disable the internal clock. */\n elapsedMs?: number;\n /** Seconds updates once per second; tenths preserves the finer-grained source timer. */\n timerPrecision?: 'seconds' | 'tenths';\n}\n\n/** Inline status for active AI work. Remount with a new key for a new operation. */\nconst ThinkingState = forwardRef<HTMLDivElement, ThinkingStateProps>(\n function ThinkingState(\n {\n label = 'Thinking',\n variant = 'drive',\n size = 'sm',\n tone = 'neutral',\n motion = 'full',\n speed = 'normal',\n showElapsed = false,\n elapsedMs,\n timerPrecision = 'seconds',\n className,\n ...props\n },\n ref\n ) {\n return (\n <div\n {...props}\n ref={ref}\n className={cn(\n 'inline-flex max-w-full items-center gap-space-2 align-middle',\n className\n )}\n >\n <LoadingIndicator\n variant={variant}\n size={size}\n tone={tone}\n animated={motion !== 'none'}\n speed={speed}\n />\n <span className=\"inline-flex min-w-0 items-baseline gap-space-2\">\n <Text\n as=\"span\"\n variant={size === 'xs' ? 'xs' : size === 'sm' ? 'sm' : 'md'}\n weight={size === 'xs' ? 'normal' : 'medium'}\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n className={cn(\n 'min-w-0 break-words',\n size === 'xs' ? 'leading-snug tracking-normal' : 'leading-4',\n motion === 'full' ? styles.shimmer : 'text-secondary',\n motion === 'full' && tone === 'brand' && styles.brandShimmer,\n speed === 'slow' && styles.slow\n )}\n >\n {label}\n </Text>\n <ThinkingStateTimer\n visible={showElapsed}\n elapsedMs={elapsedMs}\n precision={timerPrecision}\n size={size}\n />\n </span>\n </div>\n );\n }\n);\n\nexport { ThinkingState };\nexport type { ThinkingStateProps };\n"],"mappings":";;;;;;;;AAkCA,IAAM,gBAAgB,WACpB,SAAS,cACP,EACE,QAAQ,YACR,UAAU,SACV,OAAO,MACP,OAAO,WACP,SAAS,QACT,QAAQ,UACR,cAAc,OACd,WACA,iBAAiB,WACjB,WACA,GAAG,SAEL,KACA;CACA,OACE,qBAAC,OAAD;EACE,GAAI;EACC;EACL,WAAW,GACT,gEACA,SACF;EANF,UAAA,CAQE,oBAAC,kBAAD;GACW;GACH;GACA;GACN,UAAU,WAAW;GACd;EACR,CAAA,GACD,qBAAC,QAAD;GAAM,WAAU;GAAhB,UAAA,CACE,oBAAC,MAAD;IACE,IAAG;IACH,SAAS,SAAS,OAAO,OAAO,SAAS,OAAO,OAAO;IACvD,QAAQ,SAAS,OAAO,WAAW;IACnC,MAAK;IACL,aAAU;IACV,eAAY;IACZ,WAAW,GACT,uBACA,SAAS,OAAO,iCAAiC,aACjD,WAAW,SAAS,6BAAO,UAAU,kBACrC,WAAW,UAAU,SAAS,WAAW,6BAAO,cAChD,UAAU,UAAU,6BAAO,IAC7B;IAEC,UAAA;GACG,CAAA,GACN,oBAAC,oBAAD;IACE,SAAS;IACE;IACX,WAAW;IACL;GACP,CAAA,CACG;EACH,CAAA,CAAA;;AAET,CACF"}
|
|
@@ -2,7 +2,7 @@ interface ThinkingStateTimerProps {
|
|
|
2
2
|
visible: boolean;
|
|
3
3
|
elapsedMs?: number;
|
|
4
4
|
precision: 'seconds' | 'tenths';
|
|
5
|
-
size: 'sm' | 'md' | 'lg';
|
|
5
|
+
size: 'xs' | 'sm' | 'md' | 'lg';
|
|
6
6
|
}
|
|
7
7
|
/** Keeps clock updates local to the timer, without rerendering the label or grid. */
|
|
8
8
|
export declare function ThinkingStateTimer({ visible, elapsedMs, precision, size, }: ThinkingStateTimerProps): import("react").JSX.Element | null;
|
|
@@ -28,7 +28,7 @@ function ThinkingStateTimer({ visible, elapsedMs, precision, size }) {
|
|
|
28
28
|
const seconds = precision === "tenths" ? (tenths % 600 / 10).toFixed(1) : String(Math.floor(tenths % 600 / 10));
|
|
29
29
|
return /* @__PURE__ */ jsx(Text, {
|
|
30
30
|
as: "span",
|
|
31
|
-
variant: size === "sm" ? "xs" : "sm",
|
|
31
|
+
variant: size === "xs" || size === "sm" ? "xs" : "sm",
|
|
32
32
|
color: "tertiary",
|
|
33
33
|
role: "timer",
|
|
34
34
|
"aria-label": "Elapsed time",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThinkingStateTimer.js","names":[],"sources":["../../../src/components/ThinkingState/ThinkingStateTimer.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\nimport { Text } from '../Text';\n\ninterface ThinkingStateTimerProps {\n visible: boolean;\n elapsedMs?: number;\n precision: 'seconds' | 'tenths';\n size: 'sm' | 'md' | 'lg';\n}\n\n/** Keeps clock updates local to the timer, without rerendering the label or grid. */\nexport function ThinkingStateTimer({\n visible,\n elapsedMs,\n precision,\n size,\n}: ThinkingStateTimerProps) {\n const startedAt = useRef<number | null>(null);\n const [measuredMs, setMeasuredMs] = useState(0);\n const controlled = elapsedMs !== undefined;\n const intervalMs = precision === 'tenths' ? 100 : 1_000;\n\n useEffect(() => {\n startedAt.current ??= performance.now();\n const start = startedAt.current;\n if (!visible || controlled) return;\n\n // Measure time so hidden tabs and delayed callbacks do not lose elapsed time.\n const tick = () => setMeasuredMs(performance.now() - start);\n tick();\n const interval = window.setInterval(tick, intervalMs);\n return () => window.clearInterval(interval);\n }, [controlled, intervalMs, visible]);\n\n if (!visible) return null;\n\n const value = elapsedMs ?? measuredMs;\n const safeMs = Number.isFinite(value) ? Math.max(0, value) : 0;\n const tenths = Math.floor(safeMs / 100);\n const minutes = Math.floor(tenths / 600);\n const seconds =\n precision === 'tenths'\n ? ((tenths % 600) / 10).toFixed(1)\n : String(Math.floor((tenths % 600) / 10));\n\n return (\n <Text\n as=\"span\"\n variant={size === 'sm' ? 'xs' : 'sm'}\n color=\"tertiary\"\n role=\"timer\"\n aria-label=\"Elapsed time\"\n aria-live=\"off\"\n className=\"shrink-0 whitespace-nowrap font-mono tabular-nums leading-4\"\n >\n {minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`}\n </Text>\n );\n}\n"],"mappings":";;;;;AAYA,SAAgB,mBAAmB,EACjC,SACA,WACA,WACA,QAC0B;CAC1B,MAAM,YAAY,OAAsB,IAAI;CAC5C,MAAM,CAAC,YAAY,iBAAiB,SAAS,CAAC;CAC9C,MAAM,aAAa,cAAc,KAAA;CACjC,MAAM,aAAa,cAAc,WAAW,MAAM;CAElD,gBAAgB;EACd,UAAU,YAAY,YAAY,IAAI;EACtC,MAAM,QAAQ,UAAU;EACxB,IAAI,CAAC,WAAW,YAAY;EAG5B,MAAM,aAAa,cAAc,YAAY,IAAI,IAAI,KAAK;EAC1D,KAAK;EACL,MAAM,WAAW,OAAO,YAAY,MAAM,UAAU;EACpD,aAAa,OAAO,cAAc,QAAQ;CAC5C,GAAG;EAAC;EAAY;EAAY;CAAO,CAAC;CAEpC,IAAI,CAAC,SAAS,OAAO;CAErB,MAAM,QAAQ,aAAa;CAE3B,MAAM,SAAS,KAAK,OADL,OAAO,SAAS,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,KAC1B,GAAG;CACtC,MAAM,UAAU,KAAK,MAAM,SAAS,GAAG;CACvC,MAAM,UACJ,cAAc,YACR,SAAS,MAAO,GAAA,CAAI,QAAQ,CAAC,IAC/B,OAAO,KAAK,MAAO,SAAS,MAAO,EAAE,CAAC;CAE5C,OACE,oBAAC,MAAD;EACE,IAAG;EACH,SAAS,SAAS,OAAO,OAAO;
|
|
1
|
+
{"version":3,"file":"ThinkingStateTimer.js","names":[],"sources":["../../../src/components/ThinkingState/ThinkingStateTimer.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\nimport { Text } from '../Text';\n\ninterface ThinkingStateTimerProps {\n visible: boolean;\n elapsedMs?: number;\n precision: 'seconds' | 'tenths';\n size: 'xs' | 'sm' | 'md' | 'lg';\n}\n\n/** Keeps clock updates local to the timer, without rerendering the label or grid. */\nexport function ThinkingStateTimer({\n visible,\n elapsedMs,\n precision,\n size,\n}: ThinkingStateTimerProps) {\n const startedAt = useRef<number | null>(null);\n const [measuredMs, setMeasuredMs] = useState(0);\n const controlled = elapsedMs !== undefined;\n const intervalMs = precision === 'tenths' ? 100 : 1_000;\n\n useEffect(() => {\n startedAt.current ??= performance.now();\n const start = startedAt.current;\n if (!visible || controlled) return;\n\n // Measure time so hidden tabs and delayed callbacks do not lose elapsed time.\n const tick = () => setMeasuredMs(performance.now() - start);\n tick();\n const interval = window.setInterval(tick, intervalMs);\n return () => window.clearInterval(interval);\n }, [controlled, intervalMs, visible]);\n\n if (!visible) return null;\n\n const value = elapsedMs ?? measuredMs;\n const safeMs = Number.isFinite(value) ? Math.max(0, value) : 0;\n const tenths = Math.floor(safeMs / 100);\n const minutes = Math.floor(tenths / 600);\n const seconds =\n precision === 'tenths'\n ? ((tenths % 600) / 10).toFixed(1)\n : String(Math.floor((tenths % 600) / 10));\n\n return (\n <Text\n as=\"span\"\n variant={size === 'xs' || size === 'sm' ? 'xs' : 'sm'}\n color=\"tertiary\"\n role=\"timer\"\n aria-label=\"Elapsed time\"\n aria-live=\"off\"\n className=\"shrink-0 whitespace-nowrap font-mono tabular-nums leading-4\"\n >\n {minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`}\n </Text>\n );\n}\n"],"mappings":";;;;;AAYA,SAAgB,mBAAmB,EACjC,SACA,WACA,WACA,QAC0B;CAC1B,MAAM,YAAY,OAAsB,IAAI;CAC5C,MAAM,CAAC,YAAY,iBAAiB,SAAS,CAAC;CAC9C,MAAM,aAAa,cAAc,KAAA;CACjC,MAAM,aAAa,cAAc,WAAW,MAAM;CAElD,gBAAgB;EACd,UAAU,YAAY,YAAY,IAAI;EACtC,MAAM,QAAQ,UAAU;EACxB,IAAI,CAAC,WAAW,YAAY;EAG5B,MAAM,aAAa,cAAc,YAAY,IAAI,IAAI,KAAK;EAC1D,KAAK;EACL,MAAM,WAAW,OAAO,YAAY,MAAM,UAAU;EACpD,aAAa,OAAO,cAAc,QAAQ;CAC5C,GAAG;EAAC;EAAY;EAAY;CAAO,CAAC;CAEpC,IAAI,CAAC,SAAS,OAAO;CAErB,MAAM,QAAQ,aAAa;CAE3B,MAAM,SAAS,KAAK,OADL,OAAO,SAAS,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,KAC1B,GAAG;CACtC,MAAM,UAAU,KAAK,MAAM,SAAS,GAAG;CACvC,MAAM,UACJ,cAAc,YACR,SAAS,MAAO,GAAA,CAAI,QAAQ,CAAC,IAC/B,OAAO,KAAK,MAAO,SAAS,MAAO,EAAE,CAAC;CAE5C,OACE,oBAAC,MAAD;EACE,IAAG;EACH,SAAS,SAAS,QAAQ,SAAS,OAAO,OAAO;EACjD,OAAM;EACN,MAAK;EACL,cAAW;EACX,aAAU;EACV,WAAU;EAET,UAAA,UAAU,IAAI,GAAG,QAAQ,IAAI,QAAQ,KAAK,GAAG,QAAQ;CAClD,CAAA;AAEV"}
|
|
@@ -10,6 +10,8 @@ export { EmptyState } from './EmptyState/index.js';
|
|
|
10
10
|
export type { EmptyStateProps, EmptyStateSize, EmptyStateVariant, } from './EmptyState/index.js';
|
|
11
11
|
export { Avatar } from './Avatar/index.js';
|
|
12
12
|
export type { AvatarProps } from './Avatar/index.js';
|
|
13
|
+
export { HeatmapChart, HeatmapBlock } from './HeatmapChart/index.js';
|
|
14
|
+
export type { HeatmapChartProps, HeatmapBlockProps, HeatmapBlockSize, HeatmapBlockColor, } from './HeatmapChart/index.js';
|
|
13
15
|
export { Attachment, AttachmentList } from './Attachment/index.js';
|
|
14
16
|
export type { AttachmentProps, AttachmentListProps } from './Attachment/index.js';
|
|
15
17
|
export { ErrorState } from './ErrorState/index.js';
|
package/dist/components/index.js
CHANGED
|
@@ -21,6 +21,8 @@ import { getCheckedState } from "./Checkbox/getCheckedState.js";
|
|
|
21
21
|
import { Divider } from "./Divider/Divider.js";
|
|
22
22
|
import { t as ErrorState } from "../_chunks/ErrorState-CmPX01HV.js";
|
|
23
23
|
import { GroupedTabs } from "./GroupedTabs/GroupedTabs.js";
|
|
24
|
+
import { HeatmapBlock } from "./HeatmapChart/HeatmapBlock.js";
|
|
25
|
+
import { HeatmapChart } from "./HeatmapChart/HeatmapChart.js";
|
|
24
26
|
import { Input } from "./Input/Input.js";
|
|
25
27
|
import { Kbd, KbdGroup } from "./Kbd/Kbd.js";
|
|
26
28
|
import { LinearProgress } from "./LinearProgress/LinearProgress.js";
|
|
@@ -34,4 +36,4 @@ import { RadioGroupItem } from "./RadioGroup/RadioGroupItem.js";
|
|
|
34
36
|
import { Slider } from "./Slider/Slider.js";
|
|
35
37
|
import { Switch } from "./Switch/Switch.js";
|
|
36
38
|
import { Textarea } from "./Textarea/Textarea.js";
|
|
37
|
-
export { Attachment, AttachmentList, Avatar, Badge, Banner, Button, ButtonGroup, Card, ChartTooltip, ChartTooltipBody, ChartTooltipHeader, ChartTooltipRow, Checkbox, CircleSkeleton, Divider, EmptyState, ErrorState, GroupedTabs, Icon, IconButton, Input, Kbd, KbdGroup, LinearProgress, Link, MetricChart, ProgressBar, RadioButton, RadioCard, RadioGroup, RadioGroupItem, Skeleton, SkeletonRows, Slider, Spinner, SpinnerIcon, Switch, Text, Textarea, Tooltip, TooltipProvider, formatMetricCurrency, formatMetricDate, formatMetricDuration, formatMetricNumber, formatMetricTime, getCheckedState };
|
|
39
|
+
export { Attachment, AttachmentList, Avatar, Badge, Banner, Button, ButtonGroup, Card, ChartTooltip, ChartTooltipBody, ChartTooltipHeader, ChartTooltipRow, Checkbox, CircleSkeleton, Divider, EmptyState, ErrorState, GroupedTabs, HeatmapBlock, HeatmapChart, Icon, IconButton, Input, Kbd, KbdGroup, LinearProgress, Link, MetricChart, ProgressBar, RadioButton, RadioCard, RadioGroup, RadioGroupItem, Skeleton, SkeletonRows, Slider, Spinner, SpinnerIcon, Switch, Text, Textarea, Tooltip, TooltipProvider, formatMetricCurrency, formatMetricDate, formatMetricDuration, formatMetricNumber, formatMetricTime, getCheckedState };
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,8 @@ import { getCheckedState } from "./components/Checkbox/getCheckedState.js";
|
|
|
21
21
|
import { Divider } from "./components/Divider/Divider.js";
|
|
22
22
|
import { t as ErrorState } from "./_chunks/ErrorState-CmPX01HV.js";
|
|
23
23
|
import { GroupedTabs } from "./components/GroupedTabs/GroupedTabs.js";
|
|
24
|
+
import { HeatmapBlock } from "./components/HeatmapChart/HeatmapBlock.js";
|
|
25
|
+
import { HeatmapChart } from "./components/HeatmapChart/HeatmapChart.js";
|
|
24
26
|
import { Input } from "./components/Input/Input.js";
|
|
25
27
|
import { Kbd, KbdGroup } from "./components/Kbd/Kbd.js";
|
|
26
28
|
import { LinearProgress } from "./components/LinearProgress/LinearProgress.js";
|
|
@@ -35,4 +37,4 @@ import { Slider } from "./components/Slider/Slider.js";
|
|
|
35
37
|
import { Switch } from "./components/Switch/Switch.js";
|
|
36
38
|
import { Textarea } from "./components/Textarea/Textarea.js";
|
|
37
39
|
import "./components/index.js";
|
|
38
|
-
export { Attachment, AttachmentList, Avatar, Badge, Banner, Button, ButtonGroup, Card, ChartTooltip, ChartTooltipBody, ChartTooltipHeader, ChartTooltipRow, Checkbox, CircleSkeleton, Divider, EmptyState, ErrorState, GroupedTabs, Icon, IconButton, Input, Kbd, KbdGroup, LinearProgress, Link, MetricChart, ProgressBar, RadioButton, RadioCard, RadioGroup, RadioGroupItem, Skeleton, SkeletonRows, Slider, Spinner, SpinnerIcon, Switch, Text, Textarea, Tooltip, TooltipProvider, formatMetricCurrency, formatMetricDate, formatMetricDuration, formatMetricNumber, formatMetricTime, getCheckedState };
|
|
40
|
+
export { Attachment, AttachmentList, Avatar, Badge, Banner, Button, ButtonGroup, Card, ChartTooltip, ChartTooltipBody, ChartTooltipHeader, ChartTooltipRow, Checkbox, CircleSkeleton, Divider, EmptyState, ErrorState, GroupedTabs, HeatmapBlock, HeatmapChart, Icon, IconButton, Input, Kbd, KbdGroup, LinearProgress, Link, MetricChart, ProgressBar, RadioButton, RadioCard, RadioGroup, RadioGroupItem, Skeleton, SkeletonRows, Slider, Spinner, SpinnerIcon, Switch, Text, Textarea, Tooltip, TooltipProvider, formatMetricCurrency, formatMetricDate, formatMetricDuration, formatMetricNumber, formatMetricTime, getCheckedState };
|