pixelize-design-library 2.4.2-beta.59 → 2.4.2-beta.60
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/CHANGELOG.md +78 -9
- package/COMPONENT-GOTCHAS.md +118 -17
- package/DESIGN-GUIDE.md +17 -6
- package/dist/Components/AudioRecorder/AudioRecorder.d.ts +8 -0
- package/dist/Components/AudioRecorder/AudioRecorder.js +169 -0
- package/dist/Components/AudioRecorder/AudioRecorderProps.d.ts +20 -0
- package/dist/Components/AudioRecorder/AudioRecorderProps.js +2 -0
- package/dist/Components/BulkSelectionBar/BulkSelectionBar.d.ts +3 -0
- package/dist/Components/BulkSelectionBar/BulkSelectionBar.js +43 -0
- package/dist/Components/BulkSelectionBar/BulkSelectionBarProps.d.ts +35 -0
- package/dist/Components/BulkSelectionBar/BulkSelectionBarProps.js +2 -0
- package/dist/Components/BulkSelectionBar/index.d.ts +2 -0
- package/dist/Components/BulkSelectionBar/index.js +8 -0
- package/dist/Components/CalendarGrid/CalendarGrid.d.ts +3 -0
- package/dist/Components/CalendarGrid/CalendarGrid.js +173 -0
- package/dist/Components/CalendarGrid/CalendarGridProps.d.ts +54 -0
- package/dist/Components/CalendarGrid/CalendarGridProps.js +2 -0
- package/dist/Components/CalendarGrid/calendarMath.d.ts +49 -0
- package/dist/Components/CalendarGrid/calendarMath.js +178 -0
- package/dist/Components/CircularProgress/CircularProgress.d.ts +4 -0
- package/dist/Components/CircularProgress/CircularProgress.js +39 -0
- package/dist/Components/CircularProgress/CircularProgressProps.d.ts +22 -0
- package/dist/Components/CircularProgress/CircularProgressProps.js +2 -0
- package/dist/Components/CircularProgress/arc.d.ts +12 -0
- package/dist/Components/CircularProgress/arc.js +16 -0
- package/dist/Components/Common/fieldStyles.d.ts +1 -0
- package/dist/Components/Common/fieldStyles.js +2 -1
- package/dist/Components/Drawer/Drawer.d.ts +1 -1
- package/dist/Components/Drawer/Drawer.js +4 -2
- package/dist/Components/Drawer/DrawerProps.d.ts +3 -1
- package/dist/Components/EmptyState/EmptyState.js +2 -1
- package/dist/Components/EmptyState/EmptyStateProps.d.ts +2 -2
- package/dist/Components/FormDrawer/FormDrawer.d.ts +2 -0
- package/dist/Components/FormDrawer/FormDrawer.js +97 -0
- package/dist/Components/FormDrawer/FormDrawerProps.d.ts +55 -0
- package/dist/Components/FormDrawer/FormDrawerProps.js +2 -0
- package/dist/Components/LauncherTrigger/LauncherTrigger.d.ts +1 -1
- package/dist/Components/LauncherTrigger/LauncherTrigger.js +8 -1
- package/dist/Components/LauncherTrigger/LauncherTriggerProps.d.ts +2 -0
- package/dist/Components/Modal/Modal.d.ts +1 -1
- package/dist/Components/Modal/Modal.js +4 -2
- package/dist/Components/Modal/ModalProps.d.ts +3 -1
- package/dist/Components/Modal/warnDroppedSlotChildren.d.ts +2 -0
- package/dist/Components/Modal/warnDroppedSlotChildren.js +45 -0
- package/dist/Components/NavLauncher/NavLauncher.js +83 -26
- package/dist/Components/NavLauncher/NavLauncherProps.d.ts +6 -6
- package/dist/Components/NavLauncher/launcherParts.d.ts +60 -1
- package/dist/Components/NavLauncher/launcherParts.js +93 -11
- package/dist/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +5 -0
- package/dist/Components/ProfilePhotoViewer/ProfilePhotoViewerProps.d.ts +1 -1
- package/dist/Components/QueryState/QueryStateBoundary.d.ts +2 -2
- package/dist/Components/QueryState/QueryStateBoundary.js +9 -3
- package/dist/Components/QueryState/QueryStateProps.d.ts +19 -2
- package/dist/Components/SectionRail/SectionRail.d.ts +3 -0
- package/dist/Components/SectionRail/SectionRail.js +49 -0
- package/dist/Components/SectionRail/SectionRailProps.d.ts +23 -0
- package/dist/Components/SectionRail/SectionRailProps.js +2 -0
- package/dist/Components/SectionRail/index.d.ts +2 -0
- package/dist/Components/SectionRail/index.js +8 -0
- package/dist/Components/SegmentedControl/SegmentedControl.js +9 -14
- package/dist/Components/SegmentedControl/segmentedInk.d.ts +0 -2
- package/dist/Components/SegmentedControl/segmentedInk.js +1 -5
- package/dist/Components/StatCard/StatCard.d.ts +1 -1
- package/dist/Components/StatCard/StatCard.js +6 -2
- package/dist/Components/StatCard/StatCardProps.d.ts +3 -0
- package/dist/Components/StatusTag/StatusTag.d.ts +4 -0
- package/dist/Components/StatusTag/StatusTag.js +24 -0
- package/dist/Components/StatusTag/StatusTagProps.d.ts +20 -0
- package/dist/Components/StatusTag/StatusTagProps.js +2 -0
- package/dist/Components/Table/Table.d.ts +1 -1
- package/dist/Components/Table/Table.js +6 -2
- package/dist/Components/Table/TableProps.d.ts +20 -0
- package/dist/Components/Table/components/TableBody.d.ts +1 -1
- package/dist/Components/Table/components/TableBody.js +43 -18
- package/dist/Components/Table/components/TableHeader.d.ts +1 -1
- package/dist/Components/Table/components/TableHeader.js +7 -5
- package/dist/Components/Table/components/rowDetail.d.ts +32 -0
- package/dist/Components/Table/components/rowDetail.js +20 -0
- package/dist/Components/Table/hooks/useRowExpansion.d.ts +7 -0
- package/dist/Components/Table/hooks/useRowExpansion.js +27 -0
- package/dist/Components/Tabs/Tabs.d.ts +1 -1
- package/dist/Components/Tabs/Tabs.js +15 -2
- package/dist/Components/Tabs/TabsProps.d.ts +6 -2
- package/dist/Components/Tabs/components/Tab.js +4 -33
- package/dist/Components/Tabs/components/TabLinkList.d.ts +7 -0
- package/dist/Components/Tabs/components/TabLinkList.js +34 -0
- package/dist/Components/Tabs/useTabAppearance.d.ts +17 -0
- package/dist/Components/Tabs/useTabAppearance.js +67 -0
- package/dist/Components/Tag/Tag.js +11 -1
- package/dist/Components/Tag/TagProps.d.ts +4 -0
- package/dist/Components/Tag/tagDot.d.ts +3 -0
- package/dist/Components/Tag/tagDot.js +21 -0
- package/dist/esm/Components/AudioRecorder/AudioRecorder.js +161 -0
- package/dist/esm/Components/AudioRecorder/AudioRecorderProps.js +1 -0
- package/dist/esm/Components/BulkSelectionBar/BulkSelectionBar.js +38 -0
- package/dist/esm/Components/BulkSelectionBar/BulkSelectionBarProps.js +1 -0
- package/dist/esm/Components/BulkSelectionBar/index.js +1 -0
- package/dist/esm/Components/CalendarGrid/CalendarGrid.js +168 -0
- package/dist/esm/Components/CalendarGrid/CalendarGridProps.js +1 -0
- package/dist/esm/Components/CalendarGrid/calendarMath.js +155 -0
- package/dist/esm/Components/CircularProgress/CircularProgress.js +37 -0
- package/dist/esm/Components/CircularProgress/CircularProgressProps.js +1 -0
- package/dist/esm/Components/CircularProgress/arc.js +11 -0
- package/dist/esm/Components/Common/fieldStyles.js +2 -1
- package/dist/esm/Components/Drawer/Drawer.js +4 -2
- package/dist/esm/Components/EmptyState/EmptyState.js +2 -1
- package/dist/esm/Components/FormDrawer/FormDrawer.js +58 -0
- package/dist/esm/Components/FormDrawer/FormDrawerProps.js +1 -0
- package/dist/esm/Components/LauncherTrigger/LauncherTrigger.js +5 -1
- package/dist/esm/Components/Modal/Modal.js +4 -2
- package/dist/esm/Components/Modal/warnDroppedSlotChildren.js +38 -0
- package/dist/esm/Components/NavLauncher/NavLauncher.js +85 -28
- package/dist/esm/Components/NavLauncher/launcherParts.js +88 -11
- package/dist/esm/Components/ProfilePhotoViewer/ProfilePhotoViewer.js +5 -0
- package/dist/esm/Components/QueryState/QueryStateBoundary.js +10 -4
- package/dist/esm/Components/SectionRail/SectionRail.js +44 -0
- package/dist/esm/Components/SectionRail/SectionRailProps.js +1 -0
- package/dist/esm/Components/SectionRail/index.js +1 -0
- package/dist/esm/Components/SegmentedControl/SegmentedControl.js +11 -16
- package/dist/esm/Components/SegmentedControl/segmentedInk.js +0 -3
- package/dist/esm/Components/StatCard/StatCard.js +6 -2
- package/dist/esm/Components/StatusTag/StatusTag.js +19 -0
- package/dist/esm/Components/StatusTag/StatusTagProps.js +1 -0
- package/dist/esm/Components/Table/Table.js +7 -3
- package/dist/esm/Components/Table/components/TableBody.js +43 -18
- package/dist/esm/Components/Table/components/TableHeader.js +7 -5
- package/dist/esm/Components/Table/components/rowDetail.js +14 -0
- package/dist/esm/Components/Table/hooks/useRowExpansion.js +23 -0
- package/dist/esm/Components/Tabs/Tabs.js +15 -2
- package/dist/esm/Components/Tabs/components/Tab.js +4 -33
- package/dist/esm/Components/Tabs/components/TabLinkList.js +29 -0
- package/dist/esm/Components/Tabs/useTabAppearance.js +63 -0
- package/dist/esm/Components/Tag/Tag.js +12 -2
- package/dist/esm/Components/Tag/tagDot.js +17 -0
- package/dist/esm/index.js +7 -0
- package/dist/index.d.ts +16 -2
- package/dist/index.js +18 -4
- package/package.json +1 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useTabAppearance = void 0;
|
|
4
|
+
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
5
|
+
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
6
|
+
const outlineRung_1 = require("../../Theme/tokens/builders/outlineRung");
|
|
7
|
+
const color_1 = require("../../Theme/tokens/builders/color");
|
|
8
|
+
const motion_1 = require("../../Theme/tokens/primitives/motion");
|
|
9
|
+
const motion_2 = require("../../Theme/chakra/motion");
|
|
10
|
+
const controlSizes_1 = require("../../Theme/tokens/primitives/controlSizes");
|
|
11
|
+
const iconBox_1 = require("../Common/iconBox");
|
|
12
|
+
const tabStyles_1 = require("./tabStyles");
|
|
13
|
+
const TabsContext_1 = require("./TabsContext");
|
|
14
|
+
const useTabAppearance = (isSelected, isDisabled) => {
|
|
15
|
+
var _a, _b, _c;
|
|
16
|
+
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
17
|
+
const ctx = (0, TabsContext_1.useTabsContext)();
|
|
18
|
+
const isVertical = ctx.orientation === "vertical";
|
|
19
|
+
const isLine = ctx.variant === "line";
|
|
20
|
+
const canvas = colors.backgroundColor.main;
|
|
21
|
+
const scale = (0, tabStyles_1.scaleForColorScheme)(colors, ctx.colorScheme);
|
|
22
|
+
const style = (0, tabStyles_1.resolveTabStyle)(colors, scale, canvas, ctx.variant, isSelected);
|
|
23
|
+
const spec = controlSizes_1.BUTTON_SIZES[ctx.size];
|
|
24
|
+
// The list scrolls on both axes, so an outer ring is clipped into arcs; a negative offset draws it
|
|
25
|
+
// inside the tab, clear of its own border rather than thickening it.
|
|
26
|
+
const focusRing = {
|
|
27
|
+
outline: `1px solid ${(0, outlineRung_1.liftToContrast)(colors.primary[500],
|
|
28
|
+
// subtle/ghost fills are translucent tints; contrast has no meaning until they are flattened.
|
|
29
|
+
style.bg === "transparent" ? canvas : (0, color_1.compositeOver)(style.bg, canvas), outlineRung_1.AA_NON_TEXT)}`,
|
|
30
|
+
outlineOffset: "-2px",
|
|
31
|
+
};
|
|
32
|
+
const borderProps = ctx.variant === "outline"
|
|
33
|
+
? { border: "1px solid", borderColor: (_a = style.border) !== null && _a !== void 0 ? _a : "transparent" }
|
|
34
|
+
: isLine
|
|
35
|
+
? isVertical
|
|
36
|
+
? { borderLeft: "0.125rem solid", borderLeftColor: (_b = style.indicator) !== null && _b !== void 0 ? _b : "transparent" }
|
|
37
|
+
: { borderBottom: "0.125rem solid", borderBottomColor: (_c = style.indicator) !== null && _c !== void 0 ? _c : "transparent" }
|
|
38
|
+
: {};
|
|
39
|
+
const boxProps = {
|
|
40
|
+
display: "flex",
|
|
41
|
+
alignItems: "center",
|
|
42
|
+
justifyContent: "center",
|
|
43
|
+
gap: "0.375rem",
|
|
44
|
+
flexShrink: 0,
|
|
45
|
+
flex: ctx.isFitted ? 1 : undefined,
|
|
46
|
+
w: isVertical ? "100%" : undefined,
|
|
47
|
+
h: isVertical ? "auto" : spec.height,
|
|
48
|
+
minH: spec.height,
|
|
49
|
+
py: isVertical ? "0.5rem" : undefined,
|
|
50
|
+
px: spec.paddingX,
|
|
51
|
+
fontSize: spec.fontSize,
|
|
52
|
+
fontWeight: isSelected ? 600 : 500,
|
|
53
|
+
whiteSpace: "nowrap",
|
|
54
|
+
textDecoration: "none",
|
|
55
|
+
cursor: isDisabled ? "not-allowed" : "pointer",
|
|
56
|
+
opacity: isDisabled ? 0.5 : 1,
|
|
57
|
+
borderRadius: isLine ? "none" : "lg",
|
|
58
|
+
bg: style.bg,
|
|
59
|
+
color: style.color,
|
|
60
|
+
...borderProps,
|
|
61
|
+
transition: (0, motion_1.transitionFor)(["background-color", "color", "border-color"]),
|
|
62
|
+
sx: { _focusVisible: focusRing, [focusRing_styles_1.FOCUS_VISIBLE_OVER_STATE]: focusRing, ...(0, motion_2.reducedMotionSx)() },
|
|
63
|
+
_hover: !isDisabled ? { bg: style.hoverBg, color: style.hoverColor, textDecoration: "none" } : undefined,
|
|
64
|
+
};
|
|
65
|
+
return { ctx, colors, canvas, scale, style, iconSize: spec.iconSize, iconSx: (0, iconBox_1.iconBoxSx)(spec.iconSize), boxProps };
|
|
66
|
+
};
|
|
67
|
+
exports.useTabAppearance = useTabAppearance;
|
|
@@ -7,6 +7,7 @@ const motion_1 = require("../../Theme/tokens/primitives/motion");
|
|
|
7
7
|
const motion_2 = require("../../Theme/chakra/motion");
|
|
8
8
|
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
9
9
|
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
10
|
+
const tagDot_1 = require("./tagDot");
|
|
10
11
|
// NOTE: the label's pill radius would slice descenders, so clipping lives on a square child.
|
|
11
12
|
const LabelText = ({ children, title }) => ((0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", title: title, display: "block", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", borderRadius: 0, px: "0.05em", mx: "-0.05em", children: children }));
|
|
12
13
|
const ICON_SIZE = {
|
|
@@ -15,10 +16,19 @@ const ICON_SIZE = {
|
|
|
15
16
|
md: "1em",
|
|
16
17
|
lg: "1.1em",
|
|
17
18
|
};
|
|
19
|
+
const DOT_FONT_SIZE = {
|
|
20
|
+
xs: "0.75rem",
|
|
21
|
+
sm: "0.8125rem",
|
|
22
|
+
md: "0.875rem",
|
|
23
|
+
lg: "0.9375rem",
|
|
24
|
+
};
|
|
18
25
|
/** Renders a pill-shaped status/filter chip (Chakra Tag) — not interchangeable with the raw Badge primitive's static marker. */
|
|
19
|
-
const Tag = ({ label, colorScheme = "primary", tone, variant = "subtle", size = "sm", maxW, icon, leadingIcon, onTagClick, onIconClick, isDisabled, "data-testid": testId, }) => {
|
|
26
|
+
const Tag = ({ label, colorScheme = "primary", tone, variant = "subtle", display = "chip", size = "sm", maxW, icon, leadingIcon, onTagClick, onIconClick, isDisabled, "data-testid": testId, }) => {
|
|
20
27
|
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
21
28
|
const isInteractive = !!onTagClick;
|
|
29
|
+
if (display === "dot") {
|
|
30
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.Flex, { as: isInteractive ? "button" : "span", ...(isInteractive ? { type: "button", disabled: isDisabled || undefined } : {}), display: "inline-flex", alignItems: "center", gap: 2, maxW: maxW, minW: 0, borderRadius: "sm", bg: "transparent", fontSize: DOT_FONT_SIZE[size], fontWeight: 500, color: colors.text[700], cursor: isInteractive ? "pointer" : undefined, opacity: isDisabled ? 0.5 : undefined, onClick: isInteractive && !isDisabled ? onTagClick : undefined, "data-testid": testId, "data-display": "dot", "data-disabled": isDisabled || undefined, sx: isInteractive ? (0, focusRing_styles_1.focusVisibleStyles)(colors) : undefined, children: [(0, jsx_runtime_1.jsx)(react_1.Box, { as: "span", "aria-hidden": true, boxSize: "0.5rem", flexShrink: 0, borderRadius: "full", bg: (0, tagDot_1.dotColor)(colors, tone, colorScheme) }), (0, jsx_runtime_1.jsx)(react_1.Text, { as: "span", noOfLines: 1, title: maxW ? label : undefined, children: label })] }));
|
|
31
|
+
}
|
|
22
32
|
return ((0, jsx_runtime_1.jsxs)(react_1.Tag, { size: size, variant: variant, colorScheme: tone ? tone_1.toneToColorScheme[tone] : colorScheme, borderRadius: "full", maxW: maxW, "data-testid": testId, "data-interactive": isInteractive || undefined, "data-disabled": isDisabled || undefined, "aria-disabled": isDisabled || undefined, children: [leadingIcon && (0, jsx_runtime_1.jsx)(react_1.TagLeftIcon, { as: leadingIcon, boxSize: ICON_SIZE[size], "aria-hidden": true }), isInteractive ? ((0, jsx_runtime_1.jsx)(react_1.TagLabel, { as: "button", type: "button", noOfLines: undefined, disabled: isDisabled || undefined, cursor: "pointer", onClick: isDisabled ? undefined : onTagClick, children: (0, jsx_runtime_1.jsx)(LabelText, { title: maxW ? label : undefined, children: label }) })) : ((0, jsx_runtime_1.jsx)(react_1.TagLabel, { noOfLines: undefined, children: (0, jsx_runtime_1.jsx)(LabelText, { title: maxW ? label : undefined, children: label }) })), icon &&
|
|
23
33
|
(onIconClick ? ((0, jsx_runtime_1.jsx)(react_1.Box, { as: "button", type: "button", "aria-label": `Remove ${label}`, "data-testid": testId ? `${testId}-remove` : undefined, disabled: isDisabled || undefined, onClick: (e) => {
|
|
24
34
|
e.stopPropagation();
|
|
@@ -7,6 +7,8 @@ export type TagColorScheme = "primary" | "secondary" | "tertiary" | "gray" | "re
|
|
|
7
7
|
export type TagSize = "xs" | "sm" | "md" | "lg";
|
|
8
8
|
/** "solid" for emphasis, "outline" on dense/colored surfaces, "subtle" (default) for everyday chips. */
|
|
9
9
|
export type TagVariant = "solid" | "outline" | "subtle";
|
|
10
|
+
/** "chip" is the filled/outlined pill; "dot" is a tone dot beside plain text. */
|
|
11
|
+
export type TagDisplay = "chip" | "dot";
|
|
10
12
|
/** Props for the pill-shaped status/filter Tag chip — see Tag.tsx for interactive/icon behavior. */
|
|
11
13
|
export interface TagProps {
|
|
12
14
|
label: string;
|
|
@@ -26,6 +28,8 @@ export interface TagProps {
|
|
|
26
28
|
maxW?: BoxProps["maxW"];
|
|
27
29
|
size?: TagSize;
|
|
28
30
|
variant?: TagVariant;
|
|
31
|
+
/** "dot" renders an 8px tone dot plus plain label for quiet rows; variant and icons are ignored. Default "chip". */
|
|
32
|
+
display?: TagDisplay;
|
|
29
33
|
/** Dims the tag and disables its button(s) (native disabled); no effect on a purely static tag. */
|
|
30
34
|
isDisabled?: boolean;
|
|
31
35
|
/** Test hook on the root; the remove button gets `${"$"}{testId}-remove`. */
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dotColor = void 0;
|
|
4
|
+
const outlineRung_1 = require("../../Theme/tokens/builders/outlineRung");
|
|
5
|
+
const tone_1 = require("../../Theme/tokens/builders/tone");
|
|
6
|
+
const SCALE_KEYS = [
|
|
7
|
+
"primary", "secondary", "tertiary", "gray", "red", "orange", "yellow",
|
|
8
|
+
"green", "teal", "blue", "cyan", "purple", "pink",
|
|
9
|
+
];
|
|
10
|
+
const dotColor = (colors, tone, colorScheme) => {
|
|
11
|
+
const surfaces = [colors.background[50], colors.backgroundColor.main];
|
|
12
|
+
if (tone === "neutral")
|
|
13
|
+
return colors.textMuted;
|
|
14
|
+
const scale = tone
|
|
15
|
+
? colors.semantic[tone_1.toneToSemantic[tone]]
|
|
16
|
+
: SCALE_KEYS.includes(colorScheme)
|
|
17
|
+
? colors[colorScheme]
|
|
18
|
+
: colors.gray;
|
|
19
|
+
return surfaces.reduce((ink, surface) => (0, outlineRung_1.liftToContrast)(ink, surface), scale[500]);
|
|
20
|
+
};
|
|
21
|
+
exports.dotColor = dotColor;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { Box, Flex, Text, VisuallyHidden } from "@chakra-ui/react";
|
|
4
|
+
import Button from "../Button/Button.js";
|
|
5
|
+
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
6
|
+
const DEFAULT_LABELS = {
|
|
7
|
+
start: "Start recording",
|
|
8
|
+
stop: "Stop recording",
|
|
9
|
+
recording: "Recording",
|
|
10
|
+
recorded: "Recorded",
|
|
11
|
+
permissionDenied: "Microphone access was denied. Allow it in your browser settings and try again.",
|
|
12
|
+
unsupported: "Audio recording is not supported in this browser.",
|
|
13
|
+
};
|
|
14
|
+
const PREFERRED_MIME = "audio/webm";
|
|
15
|
+
const TICK_MS = 250;
|
|
16
|
+
const EXTENSIONS = {
|
|
17
|
+
"audio/webm": "webm",
|
|
18
|
+
"audio/wav": "wav",
|
|
19
|
+
"audio/wave": "wav",
|
|
20
|
+
"audio/x-wav": "wav",
|
|
21
|
+
"audio/ogg": "ogg",
|
|
22
|
+
"audio/mp4": "m4a",
|
|
23
|
+
"audio/mpeg": "mp3",
|
|
24
|
+
};
|
|
25
|
+
export const formatElapsed = (ms) => {
|
|
26
|
+
const total = Math.floor(ms / 1000);
|
|
27
|
+
return `${String(Math.floor(total / 60)).padStart(2, "0")}:${String(total % 60).padStart(2, "0")}`;
|
|
28
|
+
};
|
|
29
|
+
export const recordingFileName = (mime, now) => {
|
|
30
|
+
var _a, _b;
|
|
31
|
+
const type = mime.split(";")[0].trim().toLowerCase() || PREFERRED_MIME;
|
|
32
|
+
return { type, name: `recording-${now}.${(_a = EXTENSIONS[type]) !== null && _a !== void 0 ? _a : (((_b = type.split("/")[1]) === null || _b === void 0 ? void 0 : _b.replace(/^x-/, "")) || "webm")}` };
|
|
33
|
+
};
|
|
34
|
+
const isSupported = () => { var _a; return typeof navigator !== "undefined" && !!((_a = navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) && typeof MediaRecorder !== "undefined"; };
|
|
35
|
+
const AudioRecorder = ({ onRecorded, maxDurationSec, isDisabled = false, labels }) => {
|
|
36
|
+
const { colors } = useCustomTheme();
|
|
37
|
+
const text = { ...DEFAULT_LABELS, ...labels };
|
|
38
|
+
const [status, setStatus] = useState("idle");
|
|
39
|
+
const [elapsedMs, setElapsedMs] = useState(0);
|
|
40
|
+
const [announcement, setAnnouncement] = useState("");
|
|
41
|
+
const recorderRef = useRef(null);
|
|
42
|
+
const streamRef = useRef(null);
|
|
43
|
+
const chunksRef = useRef([]);
|
|
44
|
+
const timerRef = useRef(null);
|
|
45
|
+
const startedAtRef = useRef(0);
|
|
46
|
+
const mountedRef = useRef(true);
|
|
47
|
+
const startingRef = useRef(false);
|
|
48
|
+
const onRecordedRef = useRef(onRecorded);
|
|
49
|
+
const textRef = useRef(text);
|
|
50
|
+
onRecordedRef.current = onRecorded;
|
|
51
|
+
textRef.current = text;
|
|
52
|
+
const release = useCallback(() => {
|
|
53
|
+
var _a;
|
|
54
|
+
if (timerRef.current !== null)
|
|
55
|
+
clearInterval(timerRef.current);
|
|
56
|
+
timerRef.current = null;
|
|
57
|
+
(_a = streamRef.current) === null || _a === void 0 ? void 0 : _a.getTracks().forEach((track) => track.stop());
|
|
58
|
+
streamRef.current = null;
|
|
59
|
+
recorderRef.current = null;
|
|
60
|
+
}, []);
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
mountedRef.current = true;
|
|
63
|
+
if (!isSupported())
|
|
64
|
+
setStatus("unsupported");
|
|
65
|
+
return () => {
|
|
66
|
+
mountedRef.current = false;
|
|
67
|
+
const recorder = recorderRef.current;
|
|
68
|
+
if (recorder) {
|
|
69
|
+
recorder.ondataavailable = null;
|
|
70
|
+
recorder.onstop = null;
|
|
71
|
+
recorder.onerror = null;
|
|
72
|
+
if (recorder.state !== "inactive") {
|
|
73
|
+
try {
|
|
74
|
+
recorder.stop();
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// NOTE: a recorder the browser already tore down throws on stop.
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
release();
|
|
82
|
+
};
|
|
83
|
+
}, [release]);
|
|
84
|
+
const stop = useCallback(() => {
|
|
85
|
+
const recorder = recorderRef.current;
|
|
86
|
+
if (recorder && recorder.state !== "inactive")
|
|
87
|
+
recorder.stop();
|
|
88
|
+
}, []);
|
|
89
|
+
const start = useCallback(async () => {
|
|
90
|
+
var _a, _b;
|
|
91
|
+
if (startingRef.current)
|
|
92
|
+
return;
|
|
93
|
+
if (!isSupported()) {
|
|
94
|
+
setStatus("unsupported");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
startingRef.current = true;
|
|
98
|
+
try {
|
|
99
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
100
|
+
if (!mountedRef.current) {
|
|
101
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
streamRef.current = stream;
|
|
105
|
+
chunksRef.current = [];
|
|
106
|
+
const useWebm = (_b = (_a = MediaRecorder.isTypeSupported) === null || _a === void 0 ? void 0 : _a.call(MediaRecorder, PREFERRED_MIME)) !== null && _b !== void 0 ? _b : false;
|
|
107
|
+
const recorder = new MediaRecorder(stream, useWebm ? { mimeType: PREFERRED_MIME } : undefined);
|
|
108
|
+
recorderRef.current = recorder;
|
|
109
|
+
recorder.ondataavailable = (event) => {
|
|
110
|
+
if (event.data.size > 0)
|
|
111
|
+
chunksRef.current.push(event.data);
|
|
112
|
+
};
|
|
113
|
+
recorder.onerror = () => {
|
|
114
|
+
release();
|
|
115
|
+
setStatus("idle");
|
|
116
|
+
};
|
|
117
|
+
recorder.onstop = () => {
|
|
118
|
+
const duration = Date.now() - startedAtRef.current;
|
|
119
|
+
const { type, name } = recordingFileName(recorder.mimeType || PREFERRED_MIME, Date.now());
|
|
120
|
+
const file = new File(chunksRef.current, name, { type });
|
|
121
|
+
release();
|
|
122
|
+
setStatus("idle");
|
|
123
|
+
setElapsedMs(duration);
|
|
124
|
+
setAnnouncement(`${textRef.current.recorded} ${formatElapsed(duration)}`);
|
|
125
|
+
if (file.size > 0)
|
|
126
|
+
onRecordedRef.current(file);
|
|
127
|
+
};
|
|
128
|
+
recorder.start();
|
|
129
|
+
startedAtRef.current = Date.now();
|
|
130
|
+
setElapsedMs(0);
|
|
131
|
+
setStatus("recording");
|
|
132
|
+
setAnnouncement(textRef.current.recording);
|
|
133
|
+
timerRef.current = setInterval(() => {
|
|
134
|
+
const elapsed = Date.now() - startedAtRef.current;
|
|
135
|
+
if (maxDurationSec !== undefined && elapsed >= maxDurationSec * 1000) {
|
|
136
|
+
setElapsedMs(maxDurationSec * 1000);
|
|
137
|
+
stop();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
setElapsedMs(elapsed);
|
|
141
|
+
}, TICK_MS);
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
release();
|
|
145
|
+
if (!mountedRef.current)
|
|
146
|
+
return;
|
|
147
|
+
const name = error === null || error === void 0 ? void 0 : error.name;
|
|
148
|
+
setStatus(name === "NotFoundError" ? "unsupported" : "denied");
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
startingRef.current = false;
|
|
152
|
+
}
|
|
153
|
+
}, [maxDurationSec, release, stop]);
|
|
154
|
+
const isRecording = status === "recording";
|
|
155
|
+
const blocked = status === "unsupported";
|
|
156
|
+
return (_jsxs(Box, { children: [_jsxs(Flex, { align: "center", gap: 3, children: [_jsx(Button, { label: isRecording ? text.stop : text.start, size: "sm", colorScheme: isRecording ? "red" : undefined, variant: isRecording ? "solid" : "outline", "aria-pressed": isRecording, isDisabled: isRecording ? false : isDisabled || blocked, onClick: isRecording ? stop : () => void start() }), isRecording ? (_jsxs(Flex, { align: "center", gap: 2, children: [_jsx(Box, { "data-testid": "audio-recorder-dot", boxSize: "0.5rem", borderRadius: "full", bg: colors.semanticText.error, "aria-hidden": true, sx: {
|
|
157
|
+
"@media (prefers-reduced-motion: no-preference)": { animation: "audio-recorder-pulse 1.2s ease-in-out infinite" },
|
|
158
|
+
"@keyframes audio-recorder-pulse": { "0%, 100%": { opacity: 1 }, "50%": { opacity: 0.35 } },
|
|
159
|
+
} }), _jsx(Text, { as: "span", role: "timer", fontSize: "0.875rem", color: colors.header[900], sx: { fontVariantNumeric: "tabular-nums" }, children: formatElapsed(elapsedMs) })] })) : null] }), _jsx(VisuallyHidden, { role: "status", "aria-live": "polite", children: announcement }), status === "denied" || status === "unsupported" ? (_jsx(Text, { role: "alert", mt: 1, fontSize: "0.75rem", color: colors.semanticText.error, children: status === "denied" ? text.permissionDenied : text.unsupported })) : null] }));
|
|
160
|
+
};
|
|
161
|
+
export default AudioRecorder;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { Box, Flex, Text, VisuallyHidden } from "@chakra-ui/react";
|
|
4
|
+
import { X } from "lucide-react";
|
|
5
|
+
import Button from "../Button/Button.js";
|
|
6
|
+
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
7
|
+
const FACT_SEPARATOR = " · ";
|
|
8
|
+
const VARIANT_BY_TONE = {
|
|
9
|
+
default: "outline",
|
|
10
|
+
primary: "solid",
|
|
11
|
+
danger: "outline",
|
|
12
|
+
};
|
|
13
|
+
const SCHEME_BY_TONE = {
|
|
14
|
+
default: "gray",
|
|
15
|
+
primary: "primary",
|
|
16
|
+
danger: "red",
|
|
17
|
+
};
|
|
18
|
+
const TOUCH_SX = { "@media (pointer: coarse)": { minHeight: "44px", minWidth: "44px" } };
|
|
19
|
+
const BulkSelectionBar = ({ selectedCount, facts, actions, onClearSelection, clearLabel = "Clear selection", countLabel, placement = "sticky", "aria-label": ariaLabel = "Selection actions", "data-testid": testId = "bulk-selection-bar", }) => {
|
|
20
|
+
const { colors } = useCustomTheme();
|
|
21
|
+
const isActive = selectedCount > 0;
|
|
22
|
+
const summary = countLabel !== null && countLabel !== void 0 ? countLabel : `${selectedCount} selected`;
|
|
23
|
+
const [announcement, setAnnouncement] = useState("");
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
setAnnouncement(isActive ? summary : "");
|
|
26
|
+
}, [isActive, summary]);
|
|
27
|
+
if (!isActive)
|
|
28
|
+
return null;
|
|
29
|
+
const resolvedFacts = (facts !== null && facts !== void 0 ? facts : []).filter(Boolean);
|
|
30
|
+
const isSticky = placement === "sticky";
|
|
31
|
+
return (_jsxs(Flex, { role: "region", "aria-label": ariaLabel, "data-testid": testId, align: "center", gap: 3, wrap: "wrap", px: 4, py: 2, w: "100%", borderRadius: "lg", borderWidth: "1px", borderColor: colors.boxborder[200], bg: colors.surfaceRaised, position: isSticky ? "sticky" : "relative", bottom: isSticky ? 0 : undefined, zIndex: isSticky ? 2 : undefined, children: [_jsx(VisuallyHidden, { role: "status", "aria-live": "polite", "data-testid": "bulk-selection-announcement", children: announcement }), _jsxs(Flex, { align: "baseline", gap: 1, flex: "1", minW: "180px", wrap: "wrap", children: [_jsx(Text, { fontSize: "sm", fontWeight: 600, color: colors.text[800], "data-testid": "bulk-selection-count", children: summary }), resolvedFacts.length ? (_jsxs(Text, { fontSize: "sm", color: colors.textMuted, "data-testid": "bulk-selection-facts", children: [FACT_SEPARATOR, resolvedFacts.join(FACT_SEPARATOR)] })) : null] }), _jsxs(Flex, { align: "center", gap: 2, wrap: "wrap", children: [actions.map((action) => {
|
|
32
|
+
var _a;
|
|
33
|
+
const tone = (_a = action.tone) !== null && _a !== void 0 ? _a : "default";
|
|
34
|
+
const Icon = action.icon;
|
|
35
|
+
return (_jsx(Button, { size: "sm", variant: VARIANT_BY_TONE[tone], colorScheme: SCHEME_BY_TONE[tone], onClick: action.onClick, isDisabled: action.isDisabled, isLoading: action.isLoading, loadingText: action.label, leftIcon: Icon ? _jsx(Icon, { size: 16, "aria-hidden": "true" }) : undefined, sx: TOUCH_SX, "data-testid": `bulk-action-${action.id}`, "data-tone": tone, children: action.label }, action.id));
|
|
36
|
+
}), _jsx(Box, { children: _jsx(Button, { size: "sm", variant: "ghost", colorScheme: "gray", onClick: onClearSelection, leftIcon: _jsx(X, { size: 16, "aria-hidden": "true" }), sx: TOUCH_SX, "data-testid": "bulk-selection-clear", children: clearLabel }) })] })] }));
|
|
37
|
+
};
|
|
38
|
+
export default BulkSelectionBar;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as BulkSelectionBar } from "./BulkSelectionBar.js";
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { Box, Flex, Grid, Text } from "@chakra-ui/react";
|
|
4
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
5
|
+
import Button from "../Button/Button.js";
|
|
6
|
+
import Skeletons from "../Skeletons/Skeletons.js";
|
|
7
|
+
import { useCustomTheme } from "../../Theme/useCustomTheme.js";
|
|
8
|
+
import { focusVisibleStyles } from "../../Theme/chakra/focusRing.styles.js";
|
|
9
|
+
import { resolveSoftTone, toneScale } from "../../Theme/tokens/builders/tone.js";
|
|
10
|
+
import { dayKey, eventsOnDay, formatFullDate, formatTitle, isNavKey, isSameDay, isWeekend, layoutWeek, moveFocus, shiftAnchor, startOfDay, visibleWeeks, weekdayLabels, } from "./calendarMath.js";
|
|
11
|
+
const plural = (n, one, many) => `${n} ${n === 1 ? one : many}`;
|
|
12
|
+
const DEFAULT_LABELS = {
|
|
13
|
+
today: "Today",
|
|
14
|
+
previous: "Previous",
|
|
15
|
+
next: "Next",
|
|
16
|
+
month: "Month",
|
|
17
|
+
week: "Week",
|
|
18
|
+
day: "Day",
|
|
19
|
+
more: (count) => `+${count} more`,
|
|
20
|
+
moreLabel: (count, date) => `${plural(count, "more event", "more events")} on ${date}`,
|
|
21
|
+
eventCount: (count) => plural(count, "event", "events"),
|
|
22
|
+
};
|
|
23
|
+
const VIEWS = ["month", "week", "day"];
|
|
24
|
+
const MAX_LANES = { month: 3, week: 6, day: Infinity };
|
|
25
|
+
const MIN_CELL_HEIGHT = { month: "6.5rem", week: "12rem", day: "16rem" };
|
|
26
|
+
const LANE_HEIGHT = "1.5rem";
|
|
27
|
+
const CHIP_HEIGHT = "1.375rem";
|
|
28
|
+
const CHIP_INSET = "2px";
|
|
29
|
+
const EventChip = ({ event, colors, surface, onEventClick, segment }) => {
|
|
30
|
+
var _a;
|
|
31
|
+
const tone = resolveSoftTone(colors, toneScale(colors, (_a = event.tone) !== null && _a !== void 0 ? _a : "neutral"), surface);
|
|
32
|
+
const left = (segment === null || segment === void 0 ? void 0 : segment.continuesBefore) ? "0px" : CHIP_INSET;
|
|
33
|
+
const right = (segment === null || segment === void 0 ? void 0 : segment.continuesAfter) ? "0px" : CHIP_INSET;
|
|
34
|
+
const span = segment ? segment.endCol - segment.startCol + 1 : 1;
|
|
35
|
+
const placement = segment
|
|
36
|
+
? {
|
|
37
|
+
position: "absolute",
|
|
38
|
+
zIndex: 1,
|
|
39
|
+
top: `calc(${segment.lane} * ${LANE_HEIGHT})`,
|
|
40
|
+
left,
|
|
41
|
+
width: `calc(${span * 100}% + ${span - 1}px - ${left} - ${right})`,
|
|
42
|
+
}
|
|
43
|
+
: { width: "100%" };
|
|
44
|
+
const common = {
|
|
45
|
+
...placement,
|
|
46
|
+
display: "flex",
|
|
47
|
+
alignItems: "center",
|
|
48
|
+
h: CHIP_HEIGHT,
|
|
49
|
+
px: 2,
|
|
50
|
+
fontSize: "0.75rem",
|
|
51
|
+
fontWeight: 500,
|
|
52
|
+
lineHeight: 1,
|
|
53
|
+
textAlign: "left",
|
|
54
|
+
whiteSpace: "nowrap",
|
|
55
|
+
overflow: "hidden",
|
|
56
|
+
textOverflow: "ellipsis",
|
|
57
|
+
textDecoration: "none",
|
|
58
|
+
cursor: "pointer",
|
|
59
|
+
bg: tone.bg,
|
|
60
|
+
color: tone.fg,
|
|
61
|
+
border: "1px solid",
|
|
62
|
+
borderColor: tone.border,
|
|
63
|
+
borderLeftRadius: (segment === null || segment === void 0 ? void 0 : segment.continuesBefore) ? 0 : "md",
|
|
64
|
+
borderRightRadius: (segment === null || segment === void 0 ? void 0 : segment.continuesAfter) ? 0 : "md",
|
|
65
|
+
title: event.label,
|
|
66
|
+
"data-event-id": event.id,
|
|
67
|
+
onClick: () => onEventClick === null || onEventClick === void 0 ? void 0 : onEventClick(event),
|
|
68
|
+
_hover: { bg: tone.hoverBg, borderColor: tone.hoverBorder },
|
|
69
|
+
_active: { bg: tone.activeBg, borderColor: tone.activeBorder },
|
|
70
|
+
sx: focusVisibleStyles(colors),
|
|
71
|
+
};
|
|
72
|
+
return event.href ? (_jsx(Box, { as: "a", href: event.href, ...common, children: event.label })) : (_jsx(Box, { as: "button", type: "button", ...common, children: event.label }));
|
|
73
|
+
};
|
|
74
|
+
const CalendarGrid = ({ view, date, events, onViewChange, onDateChange, onEventClick, renderDayCell, weekStartsOn = 0, locale, isLoading = false, labels, }) => {
|
|
75
|
+
const { colors } = useCustomTheme();
|
|
76
|
+
const text = { ...DEFAULT_LABELS, ...labels };
|
|
77
|
+
const today = new Date();
|
|
78
|
+
const anchor = startOfDay(date);
|
|
79
|
+
const anchorKey = dayKey(anchor);
|
|
80
|
+
const weeks = visibleWeeks(view, anchor, weekStartsOn);
|
|
81
|
+
const columns = weeks[0].length;
|
|
82
|
+
const visibleKeys = new Set(weeks.flat().map(dayKey));
|
|
83
|
+
const shownEvents = isLoading ? [] : events;
|
|
84
|
+
const maxLanes = MAX_LANES[view];
|
|
85
|
+
const [focusedKey, setFocusedKey] = useState(anchorKey);
|
|
86
|
+
const pendingFocus = useRef(false);
|
|
87
|
+
const cellRefs = useRef(new Map());
|
|
88
|
+
const activeKey = visibleKeys.has(focusedKey) ? focusedKey : visibleKeys.has(anchorKey) ? anchorKey : dayKey(weeks[0][0]);
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
setFocusedKey(anchorKey);
|
|
91
|
+
}, [anchorKey]);
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (!pendingFocus.current)
|
|
94
|
+
return;
|
|
95
|
+
const el = cellRefs.current.get(activeKey);
|
|
96
|
+
if (!el)
|
|
97
|
+
return;
|
|
98
|
+
pendingFocus.current = false;
|
|
99
|
+
el.focus();
|
|
100
|
+
});
|
|
101
|
+
const handleCellKeyDown = (e, cellDate) => {
|
|
102
|
+
if (e.target !== e.currentTarget)
|
|
103
|
+
return;
|
|
104
|
+
if (isNavKey(e.key)) {
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
const next = moveFocus(view, cellDate, e.key, weekStartsOn);
|
|
107
|
+
const inRange = visibleKeys.has(dayKey(next));
|
|
108
|
+
if (!inRange && !onDateChange)
|
|
109
|
+
return;
|
|
110
|
+
setFocusedKey(dayKey(next));
|
|
111
|
+
pendingFocus.current = true;
|
|
112
|
+
if (!inRange)
|
|
113
|
+
onDateChange === null || onDateChange === void 0 ? void 0 : onDateChange(next);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if ((e.key === "Enter" || e.key === " ") && onDateChange) {
|
|
117
|
+
e.preventDefault();
|
|
118
|
+
onDateChange(cellDate);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const drillIntoDay = (day) => {
|
|
122
|
+
onDateChange === null || onDateChange === void 0 ? void 0 : onDateChange(day);
|
|
123
|
+
onViewChange === null || onViewChange === void 0 ? void 0 : onViewChange("day");
|
|
124
|
+
};
|
|
125
|
+
const weekdayShort = weekdayLabels(locale, weekStartsOn, "short");
|
|
126
|
+
const weekdayLong = weekdayLabels(locale, weekStartsOn, "long");
|
|
127
|
+
const headerDays = view === "day" ? [(anchor.getDay() - weekStartsOn + 7) % 7] : weekdayShort.map((_, i) => i);
|
|
128
|
+
const title = formatTitle(view, anchor, locale, weekStartsOn);
|
|
129
|
+
const template = `repeat(${columns}, minmax(0, 1fr))`;
|
|
130
|
+
const border = colors.boxborder[200];
|
|
131
|
+
const renderCell = (day, col, weekIndex, layout) => {
|
|
132
|
+
const key = dayKey(day);
|
|
133
|
+
const isToday = isSameDay(day, today);
|
|
134
|
+
const inMonth = view !== "month" || day.getMonth() === anchor.getMonth();
|
|
135
|
+
const surface = isWeekend(day) ? colors.backgroundColor.secondary : colors.backgroundColor.main;
|
|
136
|
+
const dayEvents = eventsOnDay(shownEvents, day);
|
|
137
|
+
const hidden = layout.hiddenByCol[col];
|
|
138
|
+
const active = key === activeKey;
|
|
139
|
+
const label = `${formatFullDate(day, locale)}${dayEvents.length ? `, ${text.eventCount(dayEvents.length)}` : ""}`;
|
|
140
|
+
const ownSegments = layout.segments.filter((s) => s.startCol === col && s.lane < maxLanes);
|
|
141
|
+
let body = null;
|
|
142
|
+
if (isLoading) {
|
|
143
|
+
body = (_jsx(Box, { mt: 1, "aria-hidden": true, children: (weekIndex * 7 + col) % 3 === 0 ? _jsx(Skeletons, { type: "skeleton", height: CHIP_HEIGHT, borderRadius: "md" }) : null }));
|
|
144
|
+
}
|
|
145
|
+
else if (renderDayCell) {
|
|
146
|
+
body = _jsx(Box, { mt: 1, children: renderDayCell(day, dayEvents) });
|
|
147
|
+
}
|
|
148
|
+
else if (view === "day") {
|
|
149
|
+
body = (_jsx(Flex, { direction: "column", gap: 1, mt: 2, children: dayEvents.map((event) => (_jsx(EventChip, { event: event, colors: colors, surface: surface, onEventClick: onEventClick }, event.id))) }));
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
body = (_jsxs(_Fragment, { children: [_jsx(Box, { position: "relative", mt: 1, h: `calc(${layout.visibleLanes} * ${LANE_HEIGHT})`, children: ownSegments.map((s) => (_jsx(EventChip, { event: s.event, colors: colors, surface: surface, onEventClick: onEventClick, segment: s }, s.event.id))) }), hidden > 0 ? (_jsx(Box, { as: "button", type: "button", "aria-label": text.moreLabel(hidden, formatFullDate(day, locale)), onClick: () => drillIntoDay(day), px: 1, fontSize: "0.75rem", fontWeight: 600, color: colors.header[900], textAlign: "left", textDecoration: "underline", sx: focusVisibleStyles(colors), children: text.more(hidden) })) : null] }));
|
|
153
|
+
}
|
|
154
|
+
return (_jsxs(Box, { role: "gridcell", ref: (el) => {
|
|
155
|
+
if (el)
|
|
156
|
+
cellRefs.current.set(key, el);
|
|
157
|
+
else
|
|
158
|
+
cellRefs.current.delete(key);
|
|
159
|
+
}, "data-date": key, "aria-label": label, "aria-current": isToday ? "date" : undefined, tabIndex: active ? 0 : -1, onFocus: () => setFocusedKey(key), onKeyDown: (e) => handleCellKeyDown(e, day), position: "relative", minH: MIN_CELL_HEIGHT[view], p: 1, bg: surface, borderRight: "1px solid", borderBottom: "1px solid", borderColor: border, outline: "none", _focusVisible: {
|
|
160
|
+
boxShadow: `inset 0 0 0 2px ${colors.focusRing.halo}, inset 0 0 0 4px ${colors.focusRing.ring}`,
|
|
161
|
+
}, children: [_jsx(Text, { as: "span", display: "inline-flex", alignItems: "center", justifyContent: "center", minW: "1.5rem", h: "1.5rem", px: 1, borderRadius: "full", fontSize: "0.8125rem", fontWeight: isToday ? 700 : 500, color: isToday ? colors.accentText : inMonth ? colors.header[900] : colors.textMuted, boxShadow: isToday ? `inset 0 0 0 1.5px ${colors.accentText}` : undefined, "aria-hidden": true, children: day.getDate() }), body] }, key));
|
|
162
|
+
};
|
|
163
|
+
return (_jsxs(Box, { children: [_jsxs(Flex, { align: "center", justify: "space-between", wrap: "wrap", gap: 2, mb: 3, children: [_jsxs(Flex, { align: "center", gap: 2, children: [onDateChange ? (_jsxs(_Fragment, { children: [_jsx(Button, { label: text.today, size: "sm", variant: "outline", onClick: () => onDateChange(startOfDay(new Date())) }), _jsx(Button, { "aria-label": text.previous, size: "sm", variant: "ghost", leftIcon: _jsx(ChevronLeft, { size: 16, "aria-hidden": true }), onClick: () => onDateChange(shiftAnchor(view, anchor, -1)) }), _jsx(Button, { "aria-label": text.next, size: "sm", variant: "ghost", leftIcon: _jsx(ChevronRight, { size: 16, "aria-hidden": true }), onClick: () => onDateChange(shiftAnchor(view, anchor, 1)) })] })) : null, _jsx(Text, { as: "h2", fontSize: "1rem", fontWeight: 600, color: colors.header[900], children: title })] }), onViewChange ? (_jsx(Flex, { role: "group", "aria-label": text[view], gap: 1, children: VIEWS.map((v) => (_jsx(Button, { label: text[v], size: "sm", variant: v === view ? "solid" : "outline", "aria-pressed": v === view, onClick: () => onViewChange(v) }, v))) })) : null] }), _jsx(Box, { overflowX: "auto", children: _jsxs(Box, { role: "grid", "aria-label": title, "aria-busy": isLoading || undefined, "data-view": view, minW: view === "day" ? undefined : "42rem", borderTop: "1px solid", borderLeft: "1px solid", borderColor: border, borderRadius: "lg", overflow: "hidden", children: [_jsx(Grid, { role: "row", templateColumns: template, children: headerDays.map((i) => (_jsx(Box, { role: "columnheader", "aria-label": weekdayLong[i], px: 2, py: 1, fontSize: "0.75rem", fontWeight: 600, color: colors.textMuted, bg: colors.backgroundColor.main, borderRight: "1px solid", borderBottom: "1px solid", borderColor: border, children: weekdayShort[i] }, i))) }), weeks.map((week, weekIndex) => {
|
|
164
|
+
const layout = layoutWeek(week, shownEvents, maxLanes);
|
|
165
|
+
return (_jsx(Grid, { role: "row", templateColumns: template, children: week.map((day, col) => renderCell(day, col, weekIndex, layout)) }, dayKey(week[0])));
|
|
166
|
+
})] }) })] }));
|
|
167
|
+
};
|
|
168
|
+
export default CalendarGrid;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|