eidotter 0.40.2 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -5
- package/dist/components/Accordion/components/AccordionFill.js +1 -1
- package/dist/components/Accordion/components/Section.js +1 -1
- package/dist/components/Alert/components/Alert.js +3 -2
- package/dist/components/Brand/components/Logo.d.ts +7 -7
- package/dist/components/Brand/components/Logo.js +10 -15
- package/dist/components/Brand/components/LogoGeometry.d.ts +28 -0
- package/dist/components/Brand/components/LogoGeometry.js +78 -0
- package/dist/components/Brand/components/Mark.d.ts +13 -6
- package/dist/components/Brand/components/Mark.js +14 -10
- package/dist/components/Brand/components/Wordmark.js +1 -1
- package/dist/components/Brand/components/markGeometry.d.ts +15 -0
- package/dist/components/Brand/components/markGeometry.js +20 -7
- package/dist/components/Breadcrumb/components/Breadcrumb.js +2 -2
- package/dist/components/Button/components/Button.js +1 -1
- package/dist/components/Card/components/Card.d.ts +5 -0
- package/dist/components/Card/components/Card.js +8 -2
- package/dist/components/CmdPalette/components/CmdPalette.d.ts +4 -3
- package/dist/components/CmdPalette/components/CmdPalette.js +17 -12
- package/dist/components/DecayStrip/components/DecayStrip.js +6 -1
- package/dist/components/DosDialog/components/DosDialog.d.ts +77 -0
- package/dist/components/DosDialog/components/DosDialog.js +59 -0
- package/dist/components/DosDialog/components/index.d.ts +2 -0
- package/dist/components/DosDialog/components/index.js +1 -0
- package/dist/components/DosDialog/index.d.ts +2 -0
- package/dist/components/DosDialog/index.js +1 -0
- package/dist/components/DosFigure/components/DosFigure.d.ts +2 -2
- package/dist/components/DosFigure/components/DosFigure.js +14 -7
- package/dist/components/DosWindow/components/DosWindow.d.ts +39 -0
- package/dist/components/DosWindow/components/DosWindow.js +23 -0
- package/dist/components/DosWindow/components/index.d.ts +2 -0
- package/dist/components/DosWindow/components/index.js +1 -0
- package/dist/components/DosWindow/index.d.ts +2 -0
- package/dist/components/DosWindow/index.js +1 -0
- package/dist/components/FilterBar/components/FilterBar.d.ts +7 -4
- package/dist/components/Footer/components/Footer.js +1 -1
- package/dist/components/Gauge/components/Gauge.js +19 -2
- package/dist/components/Header/components/Header.d.ts +29 -1
- package/dist/components/Header/components/Header.js +20 -5
- package/dist/components/Icon/components/Icon.d.ts +2 -0
- package/dist/components/Icon/components/Icon.js +5 -1
- package/dist/components/IconButton/components/IconButton.d.ts +44 -0
- package/dist/components/IconButton/components/IconButton.js +19 -0
- package/dist/components/IconButton/components/index.d.ts +2 -0
- package/dist/components/IconButton/components/index.js +1 -0
- package/dist/components/IconButton/index.d.ts +2 -0
- package/dist/components/IconButton/index.js +1 -0
- package/dist/components/Input/components/Input.js +1 -1
- package/dist/components/Lightbox/components/Lightbox.d.ts +10 -4
- package/dist/components/Lightbox/components/Lightbox.js +19 -11
- package/dist/components/Meter/components/Meter.js +7 -3
- package/dist/components/Meter/components/MeterGroup.js +3 -3
- package/dist/components/Modal/components/Modal.d.ts +18 -6
- package/dist/components/Modal/components/Modal.js +18 -9
- package/dist/components/Nav/components/Nav.js +5 -4
- package/dist/components/Notification/components/Notification.js +3 -2
- package/dist/components/Progress/components/Progress.js +8 -6
- package/dist/components/SeriesBars/components/SeriesBars.js +9 -8
- package/dist/components/Tag/components/Tag.d.ts +8 -1
- package/dist/components/Tag/components/Tag.js +5 -5
- package/dist/components/VitalsStrip/components/VitalsStrip.js +10 -7
- package/dist/components/registry.js +221 -19
- package/dist/eidotter.css +1 -1
- package/dist/hooks/useAnimatedDismiss.js +5 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.es.js +2174 -1600
- package/dist/index.umd.js +1 -1
- package/llms.txt +15 -7
- package/migrations/0.41.0.md +881 -0
- package/migrations/README.md +46 -0
- package/package.json +10 -2
- package/public/favicon.svg +1 -1
- package/src/styles/dos-utilities.css +6 -1
- package/src/styles/shadcn.css +52 -17
- package/src/styles/theme.amber-mono.css +4 -2
- package/src/styles/theme.amber-paper.css +106 -0
- package/src/styles/theme.cga-amber.css +3 -1
- package/src/styles/theme.cga-mode4-p0.css +3 -1
- package/src/styles/theme.cga-mode4-p1.css +3 -1
- package/src/styles/theme.cga-mode5.css +3 -1
- package/src/styles/theme.light.css +3 -1
- package/src/styles/tokens.css +5 -1
- package/tailwind.preset.cjs +23 -1
- package/dist/components/Brand/components/markGeometry.test.d.ts +0 -1
- package/dist/components/Connector/components/connectorGeometry.test.d.ts +0 -1
- package/dist/components/DecayStrip/components/resample.test.d.ts +0 -1
- package/dist/components/TimelineContainer/components/timelineUtils.test.d.ts +0 -1
- package/dist/components/TimelineContainer/components/useDrillDown.test.d.ts +0 -1
- package/dist/components/TimelineContainer/components/useSelection.test.d.ts +0 -1
- package/dist/components/TimelineContainer/components/useZoom.test.d.ts +0 -1
- package/dist/hooks/useTextScramble.test.d.ts +0 -1
- package/dist/hooks/useThemePortal.test.d.ts +0 -1
- package/dist/utils/health/formatValue.test.d.ts +0 -1
- package/dist/utils/health/healthPatterns.test.d.ts +0 -1
- package/dist/utils/health/resolveBand.test.d.ts +0 -1
- package/dist/utils/isSafeHref.test.d.ts +0 -1
- package/dist/utils/isSafeUrl.test.d.ts +0 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { PressEvent } from 'react-aria-components';
|
|
3
|
+
import type { IconName } from '../../Icon/components/Icon.js';
|
|
4
|
+
/** `md` = 36×36 hit area, 20px glyph (every built-in adopter). `window` = 28×28, 16px glyph, for dense chrome. */
|
|
5
|
+
export type IconButtonSize = 'md' | 'window';
|
|
6
|
+
/** `outline` = 1px square outline at rest (default). `bare` = no resting outline. */
|
|
7
|
+
export type IconButtonVariant = 'outline' | 'bare';
|
|
8
|
+
export interface IconButtonProps {
|
|
9
|
+
/** Glyph from the `<Icon>` set: `Close` (X), `Cancel` (minus, minimise), `Fullscreen` (maximise), `Chevron Left/Right/Up/Down`. */
|
|
10
|
+
icon: IconName;
|
|
11
|
+
/** Accessible name. Required: the button has no visible text. Rendered as `aria-label`. */
|
|
12
|
+
label: string;
|
|
13
|
+
/** Hit-area size. Defaults to `md` (36×36). */
|
|
14
|
+
size?: IconButtonSize;
|
|
15
|
+
/**
|
|
16
|
+
* `outline` (default) shows the thin square outline at rest. `bare` drops
|
|
17
|
+
* it, for places where an outline would fight a neighbouring rule.
|
|
18
|
+
*/
|
|
19
|
+
variant?: IconButtonVariant;
|
|
20
|
+
/** Disables the button (opacity 0.5, no hover invert). */
|
|
21
|
+
isDisabled?: boolean;
|
|
22
|
+
/** Called on press (mouse, touch, Enter, Space). */
|
|
23
|
+
onPress?: (e: PressEvent) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Keep the button out of the Tab order. Only for controls whose action is
|
|
26
|
+
* reachable another way (Tag's remove, reachable via Delete/Backspace on
|
|
27
|
+
* the focused tag).
|
|
28
|
+
*/
|
|
29
|
+
excludeFromTabOrder?: boolean;
|
|
30
|
+
/** Extra classes (placement) merged onto the button. */
|
|
31
|
+
className?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* IconButton: the one icon-only control (DMNC-1592).
|
|
35
|
+
*
|
|
36
|
+
* A pixel glyph from `<Icon>` inside a thin square outline: transparent fill,
|
|
37
|
+
* `text-accent` glyph at rest; hover inverts (amber fill, dark glyph); focus
|
|
38
|
+
* is a 2px brand outline offset 2px. Every close, prev/next and window
|
|
39
|
+
* control in the library uses it, so the glyph and states match everywhere.
|
|
40
|
+
*
|
|
41
|
+
* React Aria `Button` handles press, keyboard and focus-visible. Ref forwards
|
|
42
|
+
* to the `<button>`.
|
|
43
|
+
*/
|
|
44
|
+
export declare const IconButton: React.ForwardRefExoticComponent<IconButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
import { Button as AriaButton } from 'react-aria-components';
|
|
5
|
+
import { Icon } from '../../Icon/components/Icon.js';
|
|
6
|
+
import { cn } from '../../../utils/cn.js';
|
|
7
|
+
/**
|
|
8
|
+
* IconButton: the one icon-only control (DMNC-1592).
|
|
9
|
+
*
|
|
10
|
+
* A pixel glyph from `<Icon>` inside a thin square outline: transparent fill,
|
|
11
|
+
* `text-accent` glyph at rest; hover inverts (amber fill, dark glyph); focus
|
|
12
|
+
* is a 2px brand outline offset 2px. Every close, prev/next and window
|
|
13
|
+
* control in the library uses it, so the glyph and states match everywhere.
|
|
14
|
+
*
|
|
15
|
+
* React Aria `Button` handles press, keyboard and focus-visible. Ref forwards
|
|
16
|
+
* to the `<button>`.
|
|
17
|
+
*/
|
|
18
|
+
export const IconButton = forwardRef(({ icon, label, size = 'md', variant = 'outline', isDisabled, onPress, excludeFromTabOrder, className, }, ref) => (_jsx(AriaButton, { ref: ref, "aria-label": label, isDisabled: isDisabled, onPress: onPress, excludeFromTabOrder: excludeFromTabOrder, className: cn('inline-flex items-center justify-center shrink-0', 'eidotter-icon-button', size === 'window' && 'eidotter-icon-button--window', variant === 'bare' && 'eidotter-icon-button--bare', className), children: _jsx("span", { className: "eidotter-icon-button__glyph", "aria-hidden": "true", children: _jsx(Icon, { name: icon, size: "S" }) }) })));
|
|
19
|
+
IconButton.displayName = 'IconButton';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { IconButton } from './IconButton.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { IconButton } from './components/index.js';
|
|
@@ -13,6 +13,6 @@ const variantClasses = {
|
|
|
13
13
|
*/
|
|
14
14
|
export const Input = forwardRef(({ variant = 'default', label, description, errorMessage, disabled, className, onChange, isRequired, ...props }, ref) => {
|
|
15
15
|
const isInvalid = variant === 'error';
|
|
16
|
-
return (_jsxs(AriaTextField, { isDisabled: disabled, isInvalid: isInvalid, isRequired: isRequired, className: cn('eidotter-text-field', 'flex flex-col gap-1', className), children: [label && (_jsx(AriaLabel, { className: "text-dos-text-brand font-dos text-
|
|
16
|
+
return (_jsxs(AriaTextField, { isDisabled: disabled, isInvalid: isInvalid, isRequired: isRequired, className: cn('eidotter-text-field', 'flex flex-col gap-1', className), children: [label && (_jsx(AriaLabel, { className: "text-dos-text-brand font-dos text-dos-text-base uppercase tracking-wider", children: label })), _jsx(AriaInput, { ref: ref, className: cn('w-full bg-transparent text-dos-text-brand font-dos text-dos-text-base p-2', 'border-2 border-dos-border-default outline-none box-border', 'eidotter-input', variantClasses[variant] || variantClasses.default, disabled && 'eidotter-input--disabled'), onChange: onChange, ...props }), description && !isInvalid && (_jsx(AriaText, { slot: "description", className: "text-dos-text-muted font-dos text-dos-text-base", children: description })), errorMessage && isInvalid && (_jsx(AriaText, { slot: "errorMessage", className: "text-dos-text-error font-dos text-dos-text-base", children: errorMessage }))] }));
|
|
17
17
|
});
|
|
18
18
|
Input.displayName = 'Input';
|
|
@@ -13,10 +13,16 @@ export interface LightboxProps {
|
|
|
13
13
|
onIndexChange?: (index: number) => void;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
* Fullscreen image viewer
|
|
16
|
+
* Fullscreen image viewer on the DosDialog shell (DMNC-1592).
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* The shell supplies the overlay, chrome, motion and the title strip;
|
|
19
|
+
* the `[ n / N ]` counter is the strip's title and the close control sits at
|
|
20
|
+
* its end (an IconButton). Lightbox adds prev/next (IconButtons with the pixel
|
|
21
|
+
* chevrons), caption, keyboard nav, and touch swipe.
|
|
22
|
+
* It shares the shell's z-index (`--z-index-modal`) with Modal; stacking a
|
|
23
|
+
* Lightbox over an open Modal is not a supported case.
|
|
24
|
+
*
|
|
25
|
+
* The close IconButton carries `eidotter-lightbox__close`. eidotter ships no
|
|
26
|
+
* CSS for it; it is kept as a stable consumer hook for targeting the control.
|
|
21
27
|
*/
|
|
22
28
|
export declare const Lightbox: React.FC<LightboxProps>;
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { cn } from '../../../utils/cn.js';
|
|
4
|
+
import { IconButton } from '../../IconButton/components/IconButton.js';
|
|
5
|
+
import { DosDialog } from '../../DosDialog/components/DosDialog.js';
|
|
7
6
|
/**
|
|
8
|
-
* Fullscreen image viewer
|
|
7
|
+
* Fullscreen image viewer on the DosDialog shell (DMNC-1592).
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* The shell supplies the overlay, chrome, motion and the title strip;
|
|
10
|
+
* the `[ n / N ]` counter is the strip's title and the close control sits at
|
|
11
|
+
* its end (an IconButton). Lightbox adds prev/next (IconButtons with the pixel
|
|
12
|
+
* chevrons), caption, keyboard nav, and touch swipe.
|
|
13
|
+
* It shares the shell's z-index (`--z-index-modal`) with Modal; stacking a
|
|
14
|
+
* Lightbox over an open Modal is not a supported case.
|
|
15
|
+
*
|
|
16
|
+
* The close IconButton carries `eidotter-lightbox__close`. eidotter ships no
|
|
17
|
+
* CSS for it; it is kept as a stable consumer hook for targeting the control.
|
|
13
18
|
*/
|
|
14
19
|
export const Lightbox = ({ images, initialIndex = 0, isOpen, onClose, onIndexChange, }) => {
|
|
15
20
|
// Clamp the requested initial index so callers passing stale indices
|
|
@@ -78,8 +83,8 @@ export const Lightbox = ({ images, initialIndex = 0, isOpen, onClose, onIndexCha
|
|
|
78
83
|
return () => window.removeEventListener('keydown', onKeyDown);
|
|
79
84
|
}, [isOpen, goPrev, goNext]);
|
|
80
85
|
// Empty array: nothing to show. (Note: do NOT early-return on !isOpen here —
|
|
81
|
-
//
|
|
82
|
-
//
|
|
86
|
+
// the DosDialog overlay needs to stay mounted across isOpen transitions so
|
|
87
|
+
// it can play its exit animation. The overlay handles its own
|
|
83
88
|
// visibility based on the `isOpen` prop.)
|
|
84
89
|
if (images.length === 0) {
|
|
85
90
|
return null;
|
|
@@ -91,7 +96,10 @@ export const Lightbox = ({ images, initialIndex = 0, isOpen, onClose, onIndexCha
|
|
|
91
96
|
const current = images[safeIndex];
|
|
92
97
|
const hasPrev = safeIndex > 0;
|
|
93
98
|
const hasNext = safeIndex < images.length - 1;
|
|
94
|
-
return (_jsx(
|
|
95
|
-
onClose(); },
|
|
99
|
+
return (_jsx(DosDialog, { open: isOpen, onOpenChange: (open) => { if (!open)
|
|
100
|
+
onClose(); }, placement: "center", size: "full", "aria-label": current.alt || 'Image viewer', className: "eidotter-lightbox__container", head: {
|
|
101
|
+
title: (_jsxs("span", { className: "eidotter-lightbox__counter", "aria-live": "polite", children: ["[ ", safeIndex + 1, " / ", images.length, " ]"] })),
|
|
102
|
+
end: (_jsx(IconButton, { icon: "Close", label: "Close lightbox", className: "eidotter-lightbox__close", onPress: onClose })),
|
|
103
|
+
}, children: _jsxs("figure", { className: "eidotter-lightbox__figure", children: [_jsx("img", { className: "eidotter-lightbox__image", src: current.src, alt: current.alt, width: current.width, height: current.height, onTouchStart: onTouchStart, onTouchEnd: onTouchEnd }), _jsx(IconButton, { icon: "Chevron Left", label: "Previous image", className: "eidotter-lightbox__nav eidotter-lightbox__nav--prev", onPress: goPrev, isDisabled: !hasPrev }), _jsx(IconButton, { icon: "Chevron Right", label: "Next image", className: "eidotter-lightbox__nav eidotter-lightbox__nav--next", onPress: goNext, isDisabled: !hasNext }), current.caption && (_jsx("figcaption", { className: "eidotter-lightbox__caption", children: current.caption }))] }) }));
|
|
96
104
|
};
|
|
97
105
|
Lightbox.displayName = 'Lightbox';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef } from 'react';
|
|
3
3
|
import { cn } from '../../../utils/cn.js';
|
|
4
4
|
import { formatValue, isKnown, resolveBand, resolveLabels, PLACEHOLDER } from '../../../utils/health/index.js';
|
|
@@ -65,15 +65,19 @@ export const Meter = forwardRef(({ label, shortLabel, value, max = 100, unit, op
|
|
|
65
65
|
const effectivePattern = pattern ?? tonePatterns[effectiveTone] ?? 'solid';
|
|
66
66
|
const fillClass = patternClasses[effectivePattern] ?? patternClasses.solid;
|
|
67
67
|
const valueText = unknown ? PLACEHOLDER : formatValue(value, { locale, unit }).text;
|
|
68
|
+
// Number and unit render in two spans so a narrow MeterGroup can drop the
|
|
69
|
+
// unit onto its own line; the accessible value text stays one string.
|
|
70
|
+
const numberText = unknown ? PLACEHOLDER : formatValue(value, { locale }).text;
|
|
71
|
+
const unitText = unknown ? undefined : unit || undefined;
|
|
68
72
|
const valuetext = unknown ? labels.unknown : valueText;
|
|
69
73
|
const name = `${ariaLabel ?? label}: ${valuetext}`;
|
|
70
74
|
const isRange = Array.isArray(optimal);
|
|
71
75
|
const optimalProps = maxValid ? optimalStyle(optimal, max) : undefined;
|
|
72
76
|
const blockCount = segments !== undefined ? Math.max(0, Math.floor(segments)) : 0;
|
|
73
77
|
const filledBlocks = Math.round(ratio * blockCount);
|
|
74
|
-
const valuePart = showValue || overflow ? (_jsxs("div", { className: "eidotter-meter__readout flex items-center gap-1 leading-none", children: [showValue && (
|
|
78
|
+
const valuePart = showValue || overflow ? (_jsxs("div", { className: "eidotter-meter__readout flex items-center gap-1 leading-none", children: [showValue && (_jsxs("span", { className: "eidotter-meter__value text-dos-text-accent tabular-nums text-center", children: [_jsx("span", { className: "eidotter-meter__number", children: numberText }), unitText && (_jsxs(_Fragment, { children: [' ', _jsx("span", { className: "eidotter-meter__unit", children: unitText })] }))] })), overflow && (_jsx("span", { "aria-hidden": "true", className: "eidotter-meter__overflow text-dos-text-warning", children: "+" }))] })) : null;
|
|
75
79
|
const trackPart = (_jsxs("div", { className: "eidotter-meter__track relative bg-dos-bg-primary border border-dos-border-disabled", children: [blockCount > 0 ? (_jsx("div", { className: "eidotter-meter__segments", "aria-hidden": "true", children: Array.from({ length: blockCount }, (_, i) => (_jsx("span", { className: cn('eidotter-meter__segment', i < filledBlocks && `eidotter-meter__segment--filled ${fillClass}`) }, i))) })) : (_jsx("div", { className: cn('eidotter-meter__fill', fillClass), style: { '--eidotter-meter-fill': String(round4(ratio)) } })), optimalProps && (_jsx("div", { "aria-hidden": "true", className: cn('eidotter-meter__optimal', isRange && 'eidotter-meter__optimal--range'), style: optimalProps }))] }));
|
|
76
80
|
const labelPart = (_jsxs("div", { className: "eidotter-meter__caption flex flex-wrap items-center justify-center gap-1 text-dos-text-brand uppercase tracking-wider leading-tight min-w-0 max-w-full", children: [effectiveTone === 'critical' && (_jsx("span", { "aria-hidden": "true", className: "eidotter-meter__glyph text-dos-text-error", children: "\u25B2" })), _jsxs("span", { className: "eidotter-meter__label truncate", title: label, children: [_jsx("span", { className: "eidotter-meter__label--full", children: label }), shortLabel && _jsx("span", { className: "eidotter-meter__label--short", children: shortLabel })] }), resolved.label && (_jsx("span", { className: "eidotter-meter__band normal-case", children: resolved.label }))] }));
|
|
77
|
-
return (_jsx("div", { ref: ref, className: cn('eidotter-meter eidotter-health flex font-dos', orientationClasses[orientation] ?? orientationClasses.vertical, sizeClasses[size] ?? sizeClasses.sm, toneClasses[effectiveTone], className), role: "meter", "aria-valuemin": 0, "aria-valuemax": maxValid ? max : 100, "aria-valuenow": unknown ? 0 : Math.max(0, Math.min(value, max)), "aria-valuetext": valuetext, "aria-label": name, ...props, children: orientation === 'horizontal' ? (_jsxs(_Fragment, { children: [labelPart, trackPart, valuePart] })) : (_jsxs(_Fragment, { children: [valuePart, trackPart, labelPart] })) }));
|
|
81
|
+
return (_jsx("div", { ref: ref, className: cn('eidotter-meter eidotter-health flex font-dos', orientationClasses[orientation] ?? orientationClasses.vertical, sizeClasses[size] ?? sizeClasses.sm, toneClasses[effectiveTone], className), "data-has-short": shortLabel ? 'true' : 'false', role: "meter", "aria-valuemin": 0, "aria-valuemax": maxValid ? max : 100, "aria-valuenow": unknown ? 0 : Math.max(0, Math.min(value, max)), "aria-valuetext": valuetext, "aria-label": name, ...props, children: orientation === 'horizontal' ? (_jsxs(_Fragment, { children: [labelPart, trackPart, valuePart] })) : (_jsxs(_Fragment, { children: [valuePart, trackPart, labelPart] })) }));
|
|
78
82
|
});
|
|
79
83
|
Meter.displayName = 'Meter';
|
|
@@ -21,8 +21,8 @@ export const MeterGroup = forwardRef(({ layout = 'row', columns, sharedMax = fal
|
|
|
21
21
|
const fixedColumns = layout === 'grid' && columns !== undefined;
|
|
22
22
|
return (_jsx("div", { ref: ref, role: "group", className: cn('eidotter-meter-group', layoutClasses[layout] ?? layoutClasses.row, fixedColumns && 'eidotter-meter-group--columns', className), style: fixedColumns
|
|
23
23
|
? { '--eidotter-meter-columns': String(columns) }
|
|
24
|
-
: undefined, ...props, children: items
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
: undefined, ...props, children: _jsx("div", { className: "eidotter-meter-group__layout", children: items
|
|
25
|
+
? items.map((item, i) => (_jsx(Meter, { ...item, ...(scale !== undefined && { max: scale }) }, `${item.label}-${i}`)))
|
|
26
|
+
: children }) }));
|
|
27
27
|
});
|
|
28
28
|
MeterGroup.displayName = 'MeterGroup';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import type { DosDialogSize } from '../../DosDialog/components/DosDialog.js';
|
|
2
3
|
export interface ModalProps {
|
|
3
4
|
/** Whether the modal is open */
|
|
4
5
|
isOpen: boolean;
|
|
@@ -6,19 +7,30 @@ export interface ModalProps {
|
|
|
6
7
|
onOpenChange?: (isOpen: boolean) => void;
|
|
7
8
|
/** @deprecated Use `onOpenChange` instead. Called when modal should close. */
|
|
8
9
|
onClose?: () => void;
|
|
9
|
-
/** Modal title (required for accessibility) */
|
|
10
|
+
/** Modal title (required for accessibility). Rendered in the title strip. */
|
|
10
11
|
title: string;
|
|
11
12
|
/** Modal body content */
|
|
12
13
|
children: React.ReactNode;
|
|
13
14
|
/** Footer content, typically action buttons */
|
|
14
15
|
footer?: React.ReactNode;
|
|
15
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Width step from the shared dialog map (DosDialog): `sm` 24rem,
|
|
18
|
+
* `md` 30rem (default, the historical 480px), `lg` 40rem, `full`.
|
|
19
|
+
*/
|
|
20
|
+
size?: DosDialogSize;
|
|
21
|
+
/** Close on backdrop click. Escape and the close button always close. Defaults to `true`. */
|
|
22
|
+
isDismissable?: boolean;
|
|
23
|
+
/** Optional CSS class name, merged onto the dialog container */
|
|
16
24
|
className?: string;
|
|
17
25
|
}
|
|
18
26
|
/**
|
|
19
|
-
* DOS-styled Modal
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
27
|
+
* DOS-styled Modal on the DosDialog shell (DMNC-1592).
|
|
28
|
+
* The title sits in the title strip with the close control at its end; the
|
|
29
|
+
* footer keeps its 1px top rule. React Aria provides focus trapping, scroll
|
|
30
|
+
* lock, backdrop dismiss, and escape handling.
|
|
31
|
+
* Ref forwards to the dialog element (not the overlay).
|
|
32
|
+
*
|
|
33
|
+
* The close IconButton carries `eidotter-modal__close`. eidotter ships no CSS
|
|
34
|
+
* for it; it is kept as a stable consumer hook for targeting the control.
|
|
23
35
|
*/
|
|
24
36
|
export declare const Modal: React.ForwardRefExoticComponent<ModalProps & React.RefAttributes<HTMLElement>>;
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useRef, forwardRef } from 'react';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { IconButton } from '../../IconButton/components/IconButton.js';
|
|
5
|
+
import { DosDialog } from '../../DosDialog/components/DosDialog.js';
|
|
6
6
|
import { cn } from '../../../utils/cn.js';
|
|
7
7
|
/**
|
|
8
|
-
* DOS-styled Modal
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* DOS-styled Modal on the DosDialog shell (DMNC-1592).
|
|
9
|
+
* The title sits in the title strip with the close control at its end; the
|
|
10
|
+
* footer keeps its 1px top rule. React Aria provides focus trapping, scroll
|
|
11
|
+
* lock, backdrop dismiss, and escape handling.
|
|
12
|
+
* Ref forwards to the dialog element (not the overlay).
|
|
13
|
+
*
|
|
14
|
+
* The close IconButton carries `eidotter-modal__close`. eidotter ships no CSS
|
|
15
|
+
* for it; it is kept as a stable consumer hook for targeting the control.
|
|
12
16
|
*/
|
|
13
|
-
export const Modal = forwardRef(({ isOpen, onOpenChange, onClose, title, children, footer, className, }, ref) => {
|
|
17
|
+
export const Modal = forwardRef(({ isOpen, onOpenChange, onClose, title, children, footer, size = 'md', isDismissable = true, className, }, ref) => {
|
|
14
18
|
const prevOpenRef = useRef(isOpen);
|
|
15
19
|
const interactionRef = useRef(false);
|
|
16
20
|
// Notify onOpenChange on prop-driven state changes only (observer pattern for agents).
|
|
@@ -32,6 +36,11 @@ export const Modal = forwardRef(({ isOpen, onOpenChange, onClose, title, childre
|
|
|
32
36
|
if (!open)
|
|
33
37
|
onClose?.();
|
|
34
38
|
};
|
|
35
|
-
return (_jsx(
|
|
39
|
+
return (_jsx(DosDialog, { ref: ref, open: isOpen, onOpenChange: handleOpenChange, isDismissable: isDismissable, size: size, "aria-label": title, className: cn('eidotter-modal__container', className), head: {
|
|
40
|
+
title,
|
|
41
|
+
end: (_jsx(IconButton, { icon: "Close", label: "Close modal", className: "eidotter-modal__close", onPress: () => handleOpenChange(false) })),
|
|
42
|
+
},
|
|
43
|
+
/* Modal keeps its <footer> element (role=contentinfo) inside the shell slot. */
|
|
44
|
+
footer: footer ? _jsx("footer", { className: "eidotter-modal__footer", children: footer }) : undefined, children: _jsx("div", { className: "eidotter-modal__body", children: children }) }));
|
|
36
45
|
});
|
|
37
46
|
Modal.displayName = 'Modal';
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
4
4
|
import { cn } from '../../../utils/cn.js';
|
|
5
5
|
import { isSafeHref } from '../../../utils/isSafeHref.js';
|
|
6
|
-
import {
|
|
6
|
+
import { IconButton } from '../../IconButton/components/IconButton.js';
|
|
7
7
|
const variantClasses = {
|
|
8
8
|
retro: 'eidotter-nav--retro',
|
|
9
9
|
modern: 'eidotter-nav--modern',
|
|
@@ -14,7 +14,8 @@ export const DesktopNav = ({ items, activeHref, variant = 'retro', linkComponent
|
|
|
14
14
|
// Unsafe hrefs (javascript, data, vbscript, …) render the label as a
|
|
15
15
|
// plain span instead of an anchor.
|
|
16
16
|
const safeHref = isSafeHref(item.href) ? item.href : undefined;
|
|
17
|
-
|
|
17
|
+
// Retro labels render uppercase DOS, like the MENU trigger (DMNC-1584).
|
|
18
|
+
const linkClassName = cn('eidotter-nav__link', variant === 'retro' && 'uppercase', activeHref === item.href && 'eidotter-nav__link--active');
|
|
18
19
|
return (_jsx("li", { className: "eidotter-nav__desktop-item", children: safeHref ? (_jsx(LinkTag, { href: safeHref, className: linkClassName, "aria-current": activeHref === item.href ? 'page' : undefined, children: item.label })) : (_jsx("span", { className: linkClassName, children: item.label })) }, item.href));
|
|
19
20
|
}) }) }));
|
|
20
21
|
};
|
|
@@ -69,10 +70,10 @@ export const MobileNav = ({ items, activeHref, variant = 'retro', linkComponent,
|
|
|
69
70
|
first.focus();
|
|
70
71
|
}
|
|
71
72
|
};
|
|
72
|
-
return (_jsxs("div", { className: cn('eidotter-nav eidotter-nav--mobile', variantClasses[variant], className), children: [_jsx("button", { ref: triggerRef, onClick: toggle, className: "eidotter-nav__menu-trigger", "aria-label": isOpen ? 'Close menu' : 'Open menu', "aria-expanded": isOpen, "aria-controls": "eidotter-mobile-nav-panel", children: "MENU" }), isOpen && (_jsx("div", { className: "eidotter-nav__overlay", onClick: close, "aria-hidden": "true" })), _jsxs("nav", { ref: panelRef, id: "eidotter-mobile-nav-panel", className: cn('eidotter-nav__panel', isOpen && 'eidotter-nav__panel--open'), "aria-label": "Mobile navigation", inert: !isOpen, onKeyDown: handlePanelKeyDown, children: [_jsx("div", { className: "eidotter-nav__panel-header", children: _jsx(
|
|
73
|
+
return (_jsxs("div", { className: cn('eidotter-nav eidotter-nav--mobile', variantClasses[variant], className), children: [_jsx("button", { ref: triggerRef, onClick: toggle, className: "eidotter-nav__menu-trigger", "aria-label": isOpen ? 'Close menu' : 'Open menu', "aria-expanded": isOpen, "aria-controls": "eidotter-mobile-nav-panel", children: "MENU" }), isOpen && (_jsx("div", { className: "eidotter-nav__overlay", onClick: close, "aria-hidden": "true" })), _jsxs("nav", { ref: panelRef, id: "eidotter-mobile-nav-panel", className: cn('eidotter-nav__panel', isOpen && 'eidotter-nav__panel--open'), "aria-label": "Mobile navigation", inert: !isOpen, onKeyDown: handlePanelKeyDown, children: [_jsx("div", { className: "eidotter-nav__panel-header", children: _jsx(IconButton, { ref: closeButtonRef, icon: "Close", label: "Close menu", className: "eidotter-nav__close", onPress: close }) }), _jsx("ul", { className: "eidotter-nav__list", children: items.map((item) => {
|
|
73
74
|
// Same guard as desktop — mobile panel is a second render path.
|
|
74
75
|
const safeHref = isSafeHref(item.href) ? item.href : undefined;
|
|
75
|
-
const linkClassName = cn('eidotter-nav__link', activeHref === item.href && 'eidotter-nav__link--active');
|
|
76
|
+
const linkClassName = cn('eidotter-nav__link', variant === 'retro' && 'uppercase', activeHref === item.href && 'eidotter-nav__link--active');
|
|
76
77
|
return (_jsx("li", { className: "eidotter-nav__item", children: safeHref ? (_jsx(LinkTag, { href: safeHref, className: linkClassName, "aria-current": activeHref === item.href ? 'page' : undefined, onClick: close, children: item.label })) : (_jsx("span", { className: linkClassName, children: item.label })) }, item.href));
|
|
77
78
|
}) })] })] }));
|
|
78
79
|
};
|
|
@@ -4,6 +4,7 @@ import { useEffect } from 'react';
|
|
|
4
4
|
import { Button as AriaButton } from 'react-aria-components';
|
|
5
5
|
import { cn } from '../../../utils/cn.js';
|
|
6
6
|
import { Icon } from '../../Icon/components/Icon.js';
|
|
7
|
+
import { IconButton } from '../../IconButton/components/IconButton.js';
|
|
7
8
|
import { Progress } from '../../Progress/components/Progress.js';
|
|
8
9
|
import { useAnimatedDismiss } from '../../../hooks/useAnimatedDismiss.js';
|
|
9
10
|
const TYPE_ICONS = {
|
|
@@ -20,7 +21,7 @@ const TYPE_ICONS = {
|
|
|
20
21
|
* Purely presentational — consumers handle positioning.
|
|
21
22
|
*/
|
|
22
23
|
export const Notification = ({ type = 'primary', title, children, actions, onClose, avatar, image, progress, progressLabel, duration, showIcon = true, className, }) => {
|
|
23
|
-
const { isClosing, triggerClose, handleAnimationEnd } = useAnimatedDismiss('
|
|
24
|
+
const { isClosing, triggerClose, handleAnimationEnd } = useAnimatedDismiss('eidotter-pop-out', onClose);
|
|
24
25
|
// Auto-dismiss timer
|
|
25
26
|
useEffect(() => {
|
|
26
27
|
if (duration == null || !onClose)
|
|
@@ -30,5 +31,5 @@ export const Notification = ({ type = 'primary', title, children, actions, onClo
|
|
|
30
31
|
}, [duration, onClose, triggerClose]);
|
|
31
32
|
const hasIcon = showIcon && !avatar;
|
|
32
33
|
const hasProgress = progress != null;
|
|
33
|
-
return (_jsxs("div", { className: cn('w-full max-w-[400px]', 'font-dos text-dos-text-sm', 'border border-dos-border-default
|
|
34
|
+
return (_jsxs("div", { className: cn('w-full max-w-[400px]', 'font-dos text-dos-text-sm', 'border border-dos-border-default', 'eidotter-notification', `eidotter-notification--${type}`, isClosing && 'eidotter-notification--closing', className), onAnimationEnd: handleAnimationEnd, role: type === 'error' ? 'alert' : 'status', "data-type": type, children: [image && (_jsx("div", { className: "eidotter-notification__image", children: image })), _jsxs("div", { className: "flex flex-row gap-4 p-4", children: [avatar && (_jsx("div", { className: "eidotter-notification__avatar flex-shrink-0", children: avatar })), hasIcon && (_jsx("div", { className: "eidotter-notification__icon", children: _jsx(Icon, { name: TYPE_ICONS[type] || 'Info', size: "S", "aria-label": `${type} notification` }) })), _jsxs("div", { className: cn('flex-1 flex flex-col gap-3 min-w-0', onClose && !hasIcon && !avatar && 'pr-8'), children: [(title || children) && (_jsxs("div", { className: "flex flex-col gap-1", children: [title && (_jsx("div", { className: "eidotter-notification__title font-dos leading-[140%]", children: title })), children && (_jsx("div", { className: "eidotter-notification__text font-dos leading-[140%]", children: children }))] })), hasProgress && (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Progress, { value: progress, max: 100, size: "sm", fullWidth: true, showLabel: false }), progressLabel && (_jsx("div", { className: "eidotter-notification__text font-dos text-dos-text-xs", children: progressLabel }))] })), actions && actions.length > 0 && (_jsx("div", { className: "flex flex-row gap-3", children: actions.map((action, i) => (_jsxs(AriaButton, { className: "eidotter-notification__action", onPress: action.onClick, children: [action.label, action.icon] }, i))) }))] }), onClose && (_jsx(IconButton, { icon: "Close", label: "Close notification", className: "eidotter-notification__close", onPress: triggerClose }))] })] }));
|
|
34
35
|
};
|
|
@@ -5,13 +5,15 @@ const FILLED = '\u2588'; // █
|
|
|
5
5
|
const EMPTY = '\u2591'; // ░
|
|
6
6
|
const DARK_SHADE = '\u2593'; // ▓
|
|
7
7
|
const MED_SHADE = '\u2592'; // ▒
|
|
8
|
+
// eidotter tokens only: raw Tailwind sizes (text-xs/base/lg) would compile
|
|
9
|
+
// into dist/eidotter.css and override consumers' own utilities (DMNC-1579).
|
|
8
10
|
const sizeClasses = {
|
|
9
|
-
sm: 'text-
|
|
10
|
-
md: 'text-
|
|
11
|
-
lg: 'text-
|
|
12
|
-
small: 'text-
|
|
13
|
-
medium: 'text-
|
|
14
|
-
large: 'text-
|
|
11
|
+
sm: 'text-dos-text-base', // 16px
|
|
12
|
+
md: 'text-dos-text-sm', // 20px
|
|
13
|
+
lg: 'text-dos-text-md', // 22px
|
|
14
|
+
small: 'text-dos-text-base',
|
|
15
|
+
medium: 'text-dos-text-sm',
|
|
16
|
+
large: 'text-dos-text-md',
|
|
15
17
|
};
|
|
16
18
|
const variantClasses = {
|
|
17
19
|
default: 'eidotter-progress--default',
|
|
@@ -47,18 +47,19 @@ export const SeriesBars = forwardRef(({ points, max, goal, goalLabel, toneFor, t
|
|
|
47
47
|
const toneOf = (v) => thresholds
|
|
48
48
|
.filter((t) => v >= t.from)
|
|
49
49
|
.reduce((best, t) => (!best || t.from > best.from ? t : best), undefined)?.tone ?? 'neutral';
|
|
50
|
+
const goalBand = goal !== undefined && !!goalLabel;
|
|
50
51
|
const text = (v) => (isKnown(v) ? formatValue(v, { locale }).text : labels.unknown);
|
|
51
52
|
const summary = [
|
|
52
53
|
...points.map((p) => `${p.label} ${text(p.value)}`),
|
|
53
54
|
...(goal !== undefined ? [`${goalLabel ?? ''} ${text(goal)}`.trim()] : []),
|
|
54
55
|
].join(', ');
|
|
55
|
-
return (_jsxs("div", { ref: ref, className: cn('eidotter-series-bars eidotter-health relative font-dos', sizeClasses[size] ?? sizeClasses.md, className), ...props, children: [_jsxs("div", { role: "img", "aria-label": summary, children: [_jsxs("div", { className: cn('eidotter-series-bars__plot relative', plotClasses[size] ?? plotClasses.md), children: [goal !== undefined && (_jsx("div", { className: "eidotter-series-bars__goal absolute inset-x-0", style: { bottom: `${(Math.min(goal, scale) / scale) * 100}%` }
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
return (_jsxs("div", { ref: ref, className: cn('eidotter-series-bars eidotter-health relative font-dos', sizeClasses[size] ?? sizeClasses.md, className), ...props, children: [_jsxs("div", { role: "img", "aria-label": summary, children: [_jsxs("div", { className: cn('eidotter-series-bars__frame relative', goalBand && 'eidotter-series-bars__frame--goal-band'), children: [goalBand && (_jsx("span", { className: "eidotter-series-bars__goal-tag text-dos-text-brand", children: goalLabel })), _jsxs("div", { className: cn('eidotter-series-bars__plot relative', plotClasses[size] ?? plotClasses.md), children: [goal !== undefined && (_jsx("div", { className: "eidotter-series-bars__goal absolute inset-x-0", style: { bottom: `${(Math.min(goal, scale) / scale) * 100}%` } })), _jsx("div", { className: "eidotter-series-bars__cols flex items-end gap-1 h-full", children: points.map((p, i) => {
|
|
57
|
+
const v = p.value;
|
|
58
|
+
const tone = isKnown(v) ? toneOf(v) : 'neutral';
|
|
59
|
+
return (_jsx("div", { className: "eidotter-series-bars__col flex flex-col justify-end flex-1 min-w-0 h-full", title: `${p.label}: ${text(v)}`, children: isKnown(v) ? (_jsx("div", { className: cn('eidotter-series-bars__bar w-full', toneClasses[tone], patternClasses[tone]), style: {
|
|
60
|
+
height: `${(Math.min(Math.max(v, 0), scale) / scale) * 100}%`,
|
|
61
|
+
...(tone === 'neutral' && { '--eidotter-fill': NEUTRAL_FILL }),
|
|
62
|
+
} })) : (_jsx("span", { className: "eidotter-series-bars__gap text-dos-text-muted text-center", children: PLACEHOLDER })) }, i));
|
|
63
|
+
}) })] })] }), _jsx("div", { "aria-hidden": "true", className: "flex gap-1 text-dos-text-muted", children: points.map((p, i) => (_jsx("span", { className: "eidotter-series-bars__tick flex-1 min-w-0 truncate text-center tabular-nums", children: i % Math.max(tickEvery, 1) === 0 ? p.label : '' }, i))) })] }), _jsx("table", { className: "sr-only", children: _jsxs("tbody", { children: [points.map((p, i) => (_jsxs("tr", { children: [_jsx("th", { scope: "row", children: p.label }), _jsx("td", { children: text(p.value) })] }, i))), goal !== undefined && (_jsxs("tr", { children: [_jsx("th", { scope: "row", children: goalLabel }), _jsx("td", { children: text(goal) })] }))] }) })] }));
|
|
63
64
|
});
|
|
64
65
|
SeriesBars.displayName = 'SeriesBars';
|
|
@@ -6,7 +6,14 @@ export interface TagProps {
|
|
|
6
6
|
variant?: 'default' | 'outlined' | 'filled';
|
|
7
7
|
/** Size variant. Use sm/md/lg — small/medium are @deprecated aliases. */
|
|
8
8
|
size?: 'sm' | 'md' | 'lg' | 'small' | 'medium';
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* CGA color token for border and text (`default`, `outlined`), or the fill
|
|
11
|
+
* (`filled`, drawn with `text-on-brand` ink). For `filled`, pass a bright
|
|
12
|
+
* fill: a CGA primitive such as `--color-cga-bright-cyan`, `-bright-green`,
|
|
13
|
+
* `-yellow`, `-light-gray`, or `--color-semantic-background-brand`. Each
|
|
14
|
+
* clears 4.5:1 in every theme; text roles such as
|
|
15
|
+
* `--color-semantic-text-accent` drop to about 3:1 as a fill in the light theme.
|
|
16
|
+
*/
|
|
10
17
|
color?: string;
|
|
11
18
|
/** Whether the tag is selected/active */
|
|
12
19
|
selected?: boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
3
|
+
import { IconButton } from '../../IconButton/components/IconButton.js';
|
|
4
4
|
import { cn } from '../../../utils/cn.js';
|
|
5
5
|
import { useAnimatedDismiss } from '../../../hooks/useAnimatedDismiss.js';
|
|
6
6
|
const sizeClasses = {
|
|
@@ -16,7 +16,7 @@ const sizeClasses = {
|
|
|
16
16
|
*/
|
|
17
17
|
export const Tag = ({ children, variant = 'default', size = 'md', color, selected = false, closeable = false, disabled = false, onClick, onClose, className, 'aria-label': ariaLabel, ...props }) => {
|
|
18
18
|
const isInteractive = !!onClick && !disabled;
|
|
19
|
-
const { isClosing, triggerClose, handleAnimationEnd } = useAnimatedDismiss('
|
|
19
|
+
const { isClosing, triggerClose, handleAnimationEnd } = useAnimatedDismiss('eidotter-pop-out', onClose);
|
|
20
20
|
const handleKeyDown = (event) => {
|
|
21
21
|
if (disabled)
|
|
22
22
|
return;
|
|
@@ -29,18 +29,18 @@ export const Tag = ({ children, variant = 'default', size = 'md', color, selecte
|
|
|
29
29
|
triggerClose();
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
|
-
//
|
|
32
|
+
// IconButton (React Aria Button) usePress isolates the press event — it calls stopPropagation
|
|
33
33
|
// internally, preventing the close from triggering the parent Tag's onClick.
|
|
34
34
|
const handleClosePress = () => {
|
|
35
35
|
if (!disabled)
|
|
36
36
|
triggerClose();
|
|
37
37
|
};
|
|
38
38
|
const style = color ? { '--tag-color': 'var(' + color + ')' } : undefined;
|
|
39
|
-
return (_jsxs("span", { className: cn('inline-flex items-center justify-center', 'font-dos leading-none whitespace-nowrap select-none', 'rounded-dos-sm uppercase tracking-wider', sizeClasses[size] || sizeClasses.md, 'eidotter-tag', 'eidotter-tag--' + variant, selected && 'eidotter-tag--selected', disabled && 'opacity-50 cursor-not-allowed', isClosing && 'eidotter-tag--closing', isInteractive && 'cursor-pointer', className), style: style, onClick: isInteractive ? (e) => !disabled && onClick?.(e) : undefined, onKeyDown: handleKeyDown, onAnimationEnd: handleAnimationEnd, "aria-label": ariaLabel, "aria-pressed": isInteractive ? (selected || false) : undefined, "aria-disabled": disabled || undefined, "data-variant": variant, ...(isInteractive ? { role: 'button', tabIndex: 0 } : {}), ...props, children: [_jsx("span", { className: "eidotter-tag__content", children: children }), closeable && (_jsx(
|
|
39
|
+
return (_jsxs("span", { className: cn('inline-flex items-center justify-center', 'font-dos leading-none whitespace-nowrap select-none', 'rounded-dos-sm uppercase tracking-wider', sizeClasses[size] || sizeClasses.md, 'eidotter-tag', 'eidotter-tag--' + variant, selected && 'eidotter-tag--selected', disabled && 'opacity-50 cursor-not-allowed', isClosing && 'eidotter-tag--closing', isInteractive && 'cursor-pointer', className), style: style, onClick: isInteractive ? (e) => !disabled && onClick?.(e) : undefined, onKeyDown: handleKeyDown, onAnimationEnd: handleAnimationEnd, "aria-label": ariaLabel, "aria-pressed": isInteractive ? (selected || false) : undefined, "aria-disabled": disabled || undefined, "data-variant": variant, ...(isInteractive ? { role: 'button', tabIndex: 0 } : {}), ...props, children: [_jsx("span", { className: "eidotter-tag__content", children: children }), closeable && (_jsx(IconButton, { icon: "Close", label: 'Remove ' + (typeof children === 'string' ? children : 'tag'), variant: "bare", className: "eidotter-tag__close",
|
|
40
40
|
// Keep the close button out of the tab order only when the body is
|
|
41
41
|
// itself focusable (interactive) and already offers Delete/Backspace
|
|
42
42
|
// dismissal. For a closeable, non-interactive tag the body is not
|
|
43
43
|
// focusable, so the close button must stay tabbable to give keyboard
|
|
44
44
|
// users any dismiss path at all.
|
|
45
|
-
excludeFromTabOrder: isInteractive, onPress: handleClosePress, isDisabled: disabled
|
|
45
|
+
excludeFromTabOrder: isInteractive, onPress: handleClosePress, isDisabled: disabled }))] }));
|
|
46
46
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef } from 'react';
|
|
3
3
|
import { cn } from '../../../utils/cn.js';
|
|
4
4
|
import { Skeleton } from '../../Skeleton/index.js';
|
|
@@ -8,11 +8,6 @@ const sizeClasses = {
|
|
|
8
8
|
md: 'eidotter-vitals-strip--md gap-2 text-dos-text-sm',
|
|
9
9
|
lg: 'eidotter-vitals-strip--lg gap-2 text-dos-text-md',
|
|
10
10
|
};
|
|
11
|
-
const valueSizeClasses = {
|
|
12
|
-
sm: 'text-dos-display-xs',
|
|
13
|
-
md: 'text-dos-display-sm',
|
|
14
|
-
lg: 'text-dos-display-md',
|
|
15
|
-
};
|
|
16
11
|
const toneClasses = {
|
|
17
12
|
neutral: 'eidotter-tone--neutral',
|
|
18
13
|
info: 'eidotter-tone--info',
|
|
@@ -67,7 +62,15 @@ export const VitalsStrip = forwardRef(({ tiles, size = 'md', locale = 'en', labe
|
|
|
67
62
|
.filter(Boolean)
|
|
68
63
|
.join(', ');
|
|
69
64
|
return (_jsxs("div", { role: "group", "aria-label": name, "aria-busy": loading || undefined, className: cn('eidotter-vitals-strip__tile flex flex-col gap-0.5 border border-dos-border-default p-2', toneClasses[tone], statusClasses[status], tone === 'critical' &&
|
|
70
|
-
'eidotter-vitals-strip__marker border-l-4 border-dos-border-error'), children: [_jsxs("span", { className: "flex items-center gap-1 text-dos-text-brand uppercase tracking-wider leading-tight", children: [tone === 'critical' && (_jsx("span", { "aria-hidden": "true", className: "text-dos-text-error", children: "\u25B2" })), _jsx("span", { className: "truncate", title: t.label, children: t.label })] }), loading ? (_jsx("div", { "aria-hidden": "true", className: "w-full overflow-hidden", children: _jsx(Skeleton, { variant: "figure", lines: 1, animated: false }) })) : (
|
|
65
|
+
'eidotter-vitals-strip__marker border-l-4 border-dos-border-error'), children: [_jsxs("span", { className: "flex items-center gap-1 text-dos-text-brand uppercase tracking-wider leading-tight", children: [tone === 'critical' && (_jsx("span", { "aria-hidden": "true", className: "text-dos-text-error", children: "\u25B2" })), _jsx("span", { className: "truncate", title: t.label, children: t.label })] }), loading ? (_jsx("div", { "aria-hidden": "true", className: "w-full overflow-hidden", children: _jsx(Skeleton, { variant: "figure", lines: 1, animated: false }) })) : (
|
|
66
|
+
/* Display size comes from VitalsStrip.css (a per-size custom property that
|
|
67
|
+
steps down in narrow strips), not a Tailwind text-* utility: twMerge
|
|
68
|
+
would treat a second text-* class as a colour conflict and drop the
|
|
69
|
+
accent colour. The reading never wraps or widens the tile; the unit
|
|
70
|
+
moves under it when both do not fit on one line (DMNC-1573). */
|
|
71
|
+
_jsxs("span", { className: "eidotter-vitals-strip__value text-dos-text-accent leading-none tabular-nums flex flex-wrap items-baseline gap-x-1 min-w-0", children: [_jsx("span", { className: "eidotter-vitals-strip__reading min-w-0 max-w-full whitespace-nowrap overflow-hidden text-ellipsis", children: stale ? (_jsx("span", { className: "opacity-60", children: valueText })) : stateText !== null ? (
|
|
72
|
+
// State words are prose, not a reading: body size, not display size.
|
|
73
|
+
_jsx("span", { className: "eidotter-vitals-strip__state text-dos-text-sm", children: stateText })) : (valueText) }), valueText && t.unit && (_jsxs(_Fragment, { children: [' ', _jsx("span", { className: "eidotter-vitals-strip__unit whitespace-nowrap text-[0.5em] text-dos-text-brand", children: t.unit })] }))] })), stale && _jsx("span", { className: "eidotter-vitals-strip__stale", children: labels.stale }), t.secondary && !unknown && (_jsx("span", { className: "eidotter-vitals-strip__secondary text-dos-text-muted leading-tight", children: t.secondary })), resolved.label && (_jsx("span", { className: "eidotter-vitals-strip__band text-dos-text-brand leading-tight", children: resolved.label }))] }, t.id));
|
|
71
74
|
}) }));
|
|
72
75
|
});
|
|
73
76
|
VitalsStrip.displayName = 'VitalsStrip';
|