@scalewing/react 0.7.0 → 1.1.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/CHANGELOG.md +33 -0
- package/README.md +12 -2
- package/dist/components/Badge.d.ts +2 -1
- package/dist/components/Badge.js +2 -1
- package/dist/components/Box.d.ts +3 -2
- package/dist/components/Box.js +3 -2
- package/dist/components/Button.js +1 -1
- package/dist/components/DenominationGrid.d.ts +3 -0
- package/dist/components/DenominationGrid.js +12 -0
- package/dist/components/FilterChips.d.ts +1 -0
- package/dist/components/FilterChips.js +9 -3
- package/dist/components/Grid.d.ts +11 -0
- package/dist/components/Grid.js +21 -0
- package/dist/components/Inline.js +1 -1
- package/dist/components/Stack.js +1 -1
- package/dist/components/Text.d.ts +3 -0
- package/dist/components/Text.js +2 -2
- package/dist/components/denomination-grid/DenominationStrip.d.ts +4 -0
- package/dist/components/denomination-grid/DenominationStrip.js +10 -0
- package/dist/components/denomination-grid/DenominationTiles.d.ts +4 -0
- package/dist/components/denomination-grid/DenominationTiles.js +13 -0
- package/dist/components/denomination-grid/types.d.ts +25 -0
- package/dist/components/denomination-grid/types.js +1 -0
- package/dist/components/denomination-grid/validate.d.ts +2 -0
- package/dist/components/denomination-grid/validate.js +18 -0
- package/dist/css/breakpoints.d.ts +8 -0
- package/dist/css/breakpoints.js +11 -0
- package/dist/css/css-accordion.d.ts +2 -0
- package/dist/css/css-accordion.js +33 -0
- package/dist/css/css-action-menu.d.ts +2 -0
- package/dist/css/css-action-menu.js +95 -0
- package/dist/css/css-button.d.ts +7 -0
- package/dist/css/css-button.js +68 -0
- package/dist/css/css-chart.d.ts +2 -0
- package/dist/css/css-chart.js +98 -0
- package/dist/css/css-checkbox.d.ts +2 -0
- package/dist/css/css-checkbox.js +127 -0
- package/dist/css/css-chrome.d.ts +2 -0
- package/dist/css/css-chrome.js +59 -0
- package/dist/css/css-components.d.ts +1 -0
- package/dist/css/css-components.js +148 -0
- package/dist/css/css-data.d.ts +7 -0
- package/dist/css/css-data.js +189 -0
- package/dist/css/css-date-field.d.ts +2 -0
- package/dist/css/css-date-field.js +77 -0
- package/dist/css/css-denomination-grid.d.ts +2 -0
- package/dist/css/css-denomination-grid.js +213 -0
- package/dist/css/css-dialog.d.ts +2 -0
- package/dist/css/css-dialog.js +29 -0
- package/dist/css/css-document.d.ts +1 -0
- package/dist/css/css-document.js +102 -0
- package/dist/css/css-field.d.ts +2 -0
- package/dist/css/css-field.js +33 -0
- package/dist/css/css-filter-chips.d.ts +2 -0
- package/dist/css/css-filter-chips.js +116 -0
- package/dist/css/css-grid.d.ts +7 -0
- package/dist/css/css-grid.js +31 -0
- package/dist/css/css-palettes.d.ts +4 -0
- package/dist/css/css-palettes.js +52 -0
- package/dist/css/css-progress.d.ts +2 -0
- package/dist/css/css-progress.js +77 -0
- package/dist/css/css-radio-group.d.ts +2 -0
- package/dist/css/css-radio-group.js +148 -0
- package/dist/css/css-responsive.d.ts +2 -0
- package/dist/css/css-responsive.js +12 -0
- package/dist/css/css-select.d.ts +2 -0
- package/dist/css/css-select.js +134 -0
- package/dist/css/css-separator.d.ts +2 -0
- package/dist/css/css-separator.js +26 -0
- package/dist/css/css-spinner.d.ts +2 -0
- package/dist/css/css-spinner.js +45 -0
- package/dist/css/css-split.d.ts +2 -0
- package/dist/css/css-split.js +67 -0
- package/dist/css/css-switch.d.ts +2 -0
- package/dist/css/css-switch.js +115 -0
- package/dist/css/css-toast.d.ts +2 -0
- package/dist/css/css-toast.js +92 -0
- package/dist/css/css-tooltip.d.ts +2 -0
- package/dist/css/css-tooltip.js +35 -0
- package/dist/css/css-utilities.d.ts +1 -0
- package/dist/css/css-utilities.js +57 -0
- package/dist/css/css-variables.d.ts +2 -0
- package/dist/css/css-variables.js +61 -0
- package/dist/css/spacing-classes.d.ts +10 -0
- package/dist/css/spacing-classes.js +40 -0
- package/dist/css/stylesheet.d.ts +3 -0
- package/dist/css/stylesheet.js +82 -0
- package/dist/denomination-cells.d.ts +12 -0
- package/dist/denomination-cells.js +15 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.js +5 -2
- package/dist/spacing-classes.js +1 -1
- package/dist/styles.css +231 -0
- package/dist/visibility-classes.d.ts +6 -0
- package/dist/visibility-classes.js +7 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @scalewing/react
|
|
2
2
|
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f45ef5d: Generate compact `display` and `heading` sizes below the `md` breakpoint from the new tokens, so page titles and panel figures stop dominating a phone screen. No product change needed.
|
|
8
|
+
- 01e7d0e: Add `align` (`start`, `center`, `end`) to `Text`, backed by generated `sw-text-align-*` classes, and keep `DenominationGrid` tiles three per row below the `md` breakpoint.
|
|
9
|
+
- f00b3cc: Add `DenominationGrid`, a read-only count-per-unit display across a fixed column set. The `strip` layout is a captioned table with a toned row label (icon slot), muted zero cells, signed delta cells toned by sign, and an optional consumer-formatted total that moves under the row label below the `md` breakpoint; the `tiles` layout stacks the column label, count, and an optional consumer-formatted subtotal per column. Generated `sw-denomination-*` classes; invalid columns, rows, or cells throw.
|
|
10
|
+
- cc9aa28: Add an optional `count` to `FilterChipOption`. It renders as a tabular chicklet after the chip label (generated `sw-filter-chip-count`), joins the option's accessible name, and a zero count quiets the chip (`sw-filter-chip-quiet`) until it is selected. Negative or fractional counts throw.
|
|
11
|
+
- f7f3bf9: Add `Grid`, a Box-based layout that places children in one to four equal-width columns with a token gap step and an optional smaller column count below the `md` breakpoint (`columnsBelow={{ md: 2 }}`), backed by generated `sw-grid-*` classes.
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [f45ef5d]
|
|
16
|
+
- @scalewing/tokens@1.1.0
|
|
17
|
+
|
|
18
|
+
## 1.0.0
|
|
19
|
+
|
|
20
|
+
### Major Changes
|
|
21
|
+
|
|
22
|
+
- 52b45c0: Start stable versioning at 1.0.0. Packages now release independently with semantic versioning.
|
|
23
|
+
|
|
24
|
+
`@scalewing/tokens` no longer contains web CSS. It removes `generateStylesheet`, `utilityClassCatalog`, `buttonClassNames`, `badgeClassNames`, `badgeSizes`, `badgeTones`, `BadgeSize`, `BadgeTone`, `spacingClass`, `PaddingAxis`, `GapAxis`, `paddingAxes`, `gapAxes`, `breakpointScale`, `Breakpoint`, `HideDirection`, `hideClass` (never published), and the `./styles.css` and `./palette/*.css` exports. Web apps keep importing `@scalewing/react/styles.css` and `@scalewing/react/palette/<id>.css`, which are unchanged. `@scalewing/react` now generates that CSS and exports `utilityClassCatalog`, `badgeSizes`, and `badgeTones`.
|
|
25
|
+
|
|
26
|
+
### Minor Changes
|
|
27
|
+
|
|
28
|
+
- 3a3240e: Add `Box` `hideBelow` and `hideFrom` responsive visibility props backed by a `md` breakpoint token and generated `sw-hide-*` classes.
|
|
29
|
+
|
|
30
|
+
### Patch Changes
|
|
31
|
+
|
|
32
|
+
- Updated dependencies [3a3240e]
|
|
33
|
+
- Updated dependencies [52b45c0]
|
|
34
|
+
- @scalewing/tokens@1.0.0
|
|
35
|
+
|
|
3
36
|
## 0.7.0
|
|
4
37
|
|
|
5
38
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -18,7 +18,9 @@ import {
|
|
|
18
18
|
Progress,
|
|
19
19
|
Tooltip,
|
|
20
20
|
Separator,
|
|
21
|
+
DenominationGrid,
|
|
21
22
|
FilterChips,
|
|
23
|
+
Grid,
|
|
22
24
|
DateField,
|
|
23
25
|
Field,
|
|
24
26
|
Select,
|
|
@@ -42,7 +44,7 @@ import {
|
|
|
42
44
|
|
|
43
45
|
Import the CSS once at the application entry. Do not copy it into your source tree. Optional: import one `@scalewing/react/palette/<id>.css` file after it, or set `data-palette` on the `data-theme` node. React apps can set `palette` on `ThemeProvider` instead.
|
|
44
46
|
|
|
45
|
-
`Box as="a"` is a layout link. Use `Button` for press actions. `Field` associates a native `<input>` or `<select>` with a label and token gap; native text controls inherit the generated document canvas. `Select` is a labeled listbox menu when the open list must match the canvas.
|
|
47
|
+
`Box hideBelow="md"` hides a region under 48rem and `hideFrom="md"` hides it at 48rem and wider, so a product can swap a wide layout for a narrow one without its own stylesheet. Hidden regions leave the accessibility tree; do not use this to protect data. `Box as="a"` is a layout link. Use `Button` for press actions. `Field` associates a native `<input>` or `<select>` with a label and token gap; native text controls inherit the generated document canvas. `Select` is a labeled listbox menu when the open list must match the canvas.
|
|
46
48
|
|
|
47
49
|
`Field` supports optional `description`, `error`, and `required` on one native input, select, or textarea child. It preserves an existing `aria-describedby`, appends a stable message ID, and replaces the hint with an alert when an error is supplied. `error` sets `aria-invalid` and token-owned invalid styling. The consumer owns validation and localized messages.
|
|
48
50
|
|
|
@@ -62,7 +64,15 @@ Import the CSS once at the application entry. Do not copy it into your source tr
|
|
|
62
64
|
|
|
63
65
|
`Separator` divides sections with a token-colored line. It is horizontal by default; use `orientation="vertical"` in a flex row. Set `decorative` when the line only supports layout so assistive technology ignores it.
|
|
64
66
|
|
|
65
|
-
`
|
|
67
|
+
`Text` steps `display` and `heading` down to the compact token sizes below the `md` breakpoint; consumers do not size type per viewport.
|
|
68
|
+
|
|
69
|
+
`Text` takes an optional `align` (`start`, `center`, `end`) mapped to generated `sw-text-align-*` classes, for a heading that must stay centered when it wraps.
|
|
70
|
+
|
|
71
|
+
`Grid` places children in one to four equal-width columns with a token `gap` step. `columnsBelow={{ md: 2 }}` drops to fewer columns below the `md` breakpoint so tiles and stat cards stay readable on a phone. It accepts every `Box` prop.
|
|
72
|
+
|
|
73
|
+
`DenominationGrid` shows integer counts per unit across a fixed set of columns. The `strip` layout is a captioned table: each row has a toned label with an icon slot, muted zero cells, optional signed deltas toned by sign, and an optional consumer-formatted `total` that moves under the label on a phone. The `tiles` layout stacks the column label, the count, and an optional `subtotal` string per column. The primitive does no arithmetic and no currency formatting; tones reuse the Badge vocabulary. Invalid columns, rows, or cells throw.
|
|
74
|
+
|
|
75
|
+
`FilterChips` presents a long, wrapping single-choice filter set. Provide a localized group `label`, controlled `value`, `onChange`, and options with stable values and localized labels. An optional non-negative integer `count` renders as a tabular chicklet after the label and is part of the option's accessible name; a zero count quiets the chip until it is selected. Native radios provide arrow and Space navigation; disabled options are skipped.
|
|
66
76
|
|
|
67
77
|
`DateField` is a controlled native date input. Supply `value`, `min`, and `max` as valid `YYYY-MM-DD` dates; use an empty `value` for a blank field. Its callback returns a date-only string or an empty string, without timezone conversion. Invalid serialized dates and inverted bounds throw; a value outside the bounds remains visible with invalid styling. For a blank required field, supply `error` when form validation runs to show the message and invalid styling. The browser chooses the calendar and localized display.
|
|
68
78
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type BadgeSize, type BadgeTone } from '
|
|
1
|
+
import { type BadgeSize, type BadgeTone, badgeSizes, badgeTones } from '../css/css-data.js';
|
|
2
2
|
import { type HTMLAttributes, type ReactNode } from 'react';
|
|
3
|
+
export { badgeSizes, badgeTones };
|
|
3
4
|
export type { BadgeSize, BadgeTone };
|
|
4
5
|
export type BadgeProps = HTMLAttributes<HTMLSpanElement> & {
|
|
5
6
|
children: ReactNode;
|
package/dist/components/Badge.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { badgeClassNames, } from '
|
|
2
|
+
import { badgeClassNames, badgeSizes, badgeTones, } from '../css/css-data.js';
|
|
3
3
|
import { forwardRef } from 'react';
|
|
4
4
|
import { cx } from '../class-names.js';
|
|
5
|
+
export { badgeSizes, badgeTones };
|
|
5
6
|
export const Badge = forwardRef(function Badge({ children, className, size = 'md', tone = 'neutral', ...rest }, ref) {
|
|
6
7
|
return (_jsx("span", { ref: ref, className: cx(...badgeClassNames(tone, size), className), ...rest, children: children }));
|
|
7
8
|
});
|
package/dist/components/Box.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { type RadiusStep, type SemanticColorKey } from '@scalewing/tokens';
|
|
2
2
|
import { type AnchorHTMLAttributes, type HTMLAttributes } from 'react';
|
|
3
3
|
import { type SpacingProps } from '../spacing-classes.js';
|
|
4
|
+
import { type VisibilityProps } from '../visibility-classes.js';
|
|
4
5
|
export type BoxElement = 'div' | 'section' | 'article' | 'header' | 'footer' | 'main' | 'nav' | 'aside' | 'span' | 'a' | 'label';
|
|
5
|
-
export type BoxProps = SpacingProps & HTMLAttributes<HTMLElement> & Pick<AnchorHTMLAttributes<HTMLAnchorElement>, 'download' | 'href' | 'rel' | 'target'> & {
|
|
6
|
+
export type BoxProps = SpacingProps & VisibilityProps & HTMLAttributes<HTMLElement> & Pick<AnchorHTMLAttributes<HTMLAnchorElement>, 'download' | 'href' | 'rel' | 'target'> & {
|
|
6
7
|
as?: BoxElement;
|
|
7
8
|
background?: Extract<SemanticColorKey, 'background' | 'surface'>;
|
|
8
9
|
radius?: RadiusStep;
|
|
9
10
|
border?: boolean;
|
|
10
11
|
};
|
|
11
|
-
export declare const Box: import("react").ForwardRefExoticComponent<SpacingProps & HTMLAttributes<HTMLElement> & Pick<AnchorHTMLAttributes<HTMLAnchorElement>, "download" | "href" | "rel" | "target"> & {
|
|
12
|
+
export declare const Box: import("react").ForwardRefExoticComponent<SpacingProps & VisibilityProps & HTMLAttributes<HTMLElement> & Pick<AnchorHTMLAttributes<HTMLAnchorElement>, "download" | "href" | "rel" | "target"> & {
|
|
12
13
|
as?: BoxElement;
|
|
13
14
|
background?: Extract<SemanticColorKey, "background" | "surface">;
|
|
14
15
|
radius?: RadiusStep;
|
package/dist/components/Box.js
CHANGED
|
@@ -2,7 +2,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { forwardRef, } from 'react';
|
|
3
3
|
import { cx } from '../class-names.js';
|
|
4
4
|
import { spacingClassNames } from '../spacing-classes.js';
|
|
5
|
-
|
|
5
|
+
import { visibilityClassNames, } from '../visibility-classes.js';
|
|
6
|
+
export const Box = forwardRef(function Box({ as = 'div', background, border, className, hideBelow, hideFrom, padding, paddingBottom, paddingLeft, paddingRight, paddingTop, paddingX, paddingY, radius, style, ...rest }, ref) {
|
|
6
7
|
const Component = as;
|
|
7
8
|
return (_jsx(Component, { ref: ref, className: cx(...spacingClassNames({
|
|
8
9
|
padding,
|
|
@@ -12,7 +13,7 @@ export const Box = forwardRef(function Box({ as = 'div', background, border, cla
|
|
|
12
13
|
paddingTop,
|
|
13
14
|
paddingX,
|
|
14
15
|
paddingY,
|
|
15
|
-
}), className), style: {
|
|
16
|
+
}), ...visibilityClassNames({ hideBelow, hideFrom }), className), style: {
|
|
16
17
|
backgroundColor: background
|
|
17
18
|
? `var(--sw-color-${background})`
|
|
18
19
|
: undefined,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { buttonClassNames
|
|
2
|
+
import { buttonClassNames } from '../css/css-button.js';
|
|
3
3
|
import { forwardRef } from 'react';
|
|
4
4
|
import { cx } from '../class-names.js';
|
|
5
5
|
export const Button = forwardRef(function Button({ children, className, disabled = false, onPress, size = 'md', type = 'button', variant = 'primary', ...rest }, ref) {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type DenominationGridProps } from './denomination-grid/types.js';
|
|
2
|
+
export type { DenominationGridColumn, DenominationGridLayout, DenominationGridProps, DenominationGridRow, DenominationGridTone, } from './denomination-grid/types.js';
|
|
3
|
+
export declare function DenominationGrid({ label, columns, rows, layout, subtotal, zeroLabel, }: DenominationGridProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { DenominationStrip } from './denomination-grid/DenominationStrip.js';
|
|
3
|
+
import { DenominationTiles } from './denomination-grid/DenominationTiles.js';
|
|
4
|
+
import { assertDenominationGrid } from './denomination-grid/validate.js';
|
|
5
|
+
export function DenominationGrid({ label, columns, rows, layout = 'strip', subtotal, zeroLabel = '—', }) {
|
|
6
|
+
assertDenominationGrid(label, columns, rows);
|
|
7
|
+
if (!zeroLabel)
|
|
8
|
+
throw new RangeError('zeroLabel must not be empty');
|
|
9
|
+
if (layout === 'tiles')
|
|
10
|
+
return (_jsx(DenominationTiles, { columns: columns, label: label, rows: rows, subtotal: subtotal, zeroLabel: zeroLabel }));
|
|
11
|
+
return (_jsx(DenominationStrip, { columns: columns, label: label, rows: rows, zeroLabel: zeroLabel }));
|
|
12
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useId } from 'react';
|
|
4
|
+
import { cx } from '../class-names.js';
|
|
5
|
+
function isCount(count) {
|
|
6
|
+
return count === undefined || (Number.isInteger(count) && count >= 0);
|
|
7
|
+
}
|
|
4
8
|
export function FilterChips({ label, value, onChange, options, }) {
|
|
5
9
|
const name = useId();
|
|
6
10
|
const values = options.map((option) => option.value);
|
|
@@ -13,8 +17,10 @@ export function FilterChips({ label, value, onChange, options, }) {
|
|
|
13
17
|
throw new RangeError('options must have unique values');
|
|
14
18
|
if (!values.includes(value))
|
|
15
19
|
throw new RangeError('value must match an option');
|
|
16
|
-
|
|
20
|
+
if (options.some((option) => !isCount(option.count)))
|
|
21
|
+
throw new RangeError('count must be a non-negative integer');
|
|
22
|
+
return (_jsxs("fieldset", { className: "sw-filter-chips", children: [_jsx("legend", { className: "sw-filter-chips-legend", children: label }), _jsx("span", { className: "sw-filter-chips-options", children: options.map((option) => (_jsxs("label", { className: cx('sw-filter-chip', option.count === 0 && 'sw-filter-chip-quiet'), children: [_jsx("input", { checked: option.value === value, className: "sw-filter-chip-input", disabled: option.disabled, name: name, onChange: (event) => {
|
|
17
23
|
if (event.currentTarget.checked && !option.disabled)
|
|
18
24
|
onChange(option.value);
|
|
19
|
-
}, type: "radio", value: option.value }),
|
|
25
|
+
}, type: "radio", value: option.value }), _jsxs("span", { className: "sw-filter-chip-face", children: [option.label, option.count === undefined ? null : (_jsxs(_Fragment, { children: [' ', _jsx("span", { className: "sw-filter-chip-count", children: option.count })] }))] })] }, option.value))) })] }));
|
|
20
26
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type SpacingStep } from '@scalewing/tokens';
|
|
2
|
+
import { type Breakpoint } from '../css/breakpoints.js';
|
|
3
|
+
import { type GridColumns } from '../css/css-grid.js';
|
|
4
|
+
import { type BoxProps } from './Box.js';
|
|
5
|
+
export type { GridColumns };
|
|
6
|
+
export type GridProps = BoxProps & {
|
|
7
|
+
columns?: GridColumns;
|
|
8
|
+
columnsBelow?: Partial<Record<Breakpoint, GridColumns>>;
|
|
9
|
+
gap?: SpacingStep;
|
|
10
|
+
};
|
|
11
|
+
export declare function Grid({ className, columns, columnsBelow, gap, ...rest }: GridProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from '../class-names.js';
|
|
3
|
+
import { breakpoints } from '../css/breakpoints.js';
|
|
4
|
+
import { gridColumnCounts, gridColumnsBelowClass, gridColumnsClass, } from '../css/css-grid.js';
|
|
5
|
+
import { spacingClass } from '../css/spacing-classes.js';
|
|
6
|
+
import { Box } from './Box.js';
|
|
7
|
+
function assertColumns(columns) {
|
|
8
|
+
if (!gridColumnCounts.includes(columns))
|
|
9
|
+
throw new RangeError(`columns must be one of ${gridColumnCounts.join(', ')}`);
|
|
10
|
+
}
|
|
11
|
+
export function Grid({ className, columns = 1, columnsBelow, gap = 0, ...rest }) {
|
|
12
|
+
assertColumns(columns);
|
|
13
|
+
const belowClasses = breakpoints.flatMap((breakpoint) => {
|
|
14
|
+
const count = columnsBelow?.[breakpoint];
|
|
15
|
+
if (count === undefined)
|
|
16
|
+
return [];
|
|
17
|
+
assertColumns(count);
|
|
18
|
+
return [gridColumnsBelowClass(breakpoint, count)];
|
|
19
|
+
});
|
|
20
|
+
return (_jsx(Box, { className: cx('sw-grid', gridColumnsClass(columns), ...belowClasses, spacingClass('gap', 'all', gap), className), ...rest }));
|
|
21
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { spacingClass } from '
|
|
2
|
+
import { spacingClass } from '../css/spacing-classes.js';
|
|
3
3
|
import { cx } from '../class-names.js';
|
|
4
4
|
import { Box } from './Box.js';
|
|
5
5
|
export function Inline({ align = 'center', className, gap = 0, justify = 'start', wrap = false, ...rest }) {
|
package/dist/components/Stack.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { spacingClass } from '
|
|
2
|
+
import { spacingClass } from '../css/spacing-classes.js';
|
|
3
3
|
import { cx } from '../class-names.js';
|
|
4
4
|
import { Box } from './Box.js';
|
|
5
5
|
export function Stack({ align = 'stretch', className, gap = 0, justify = 'start', ...rest }) {
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { type SemanticColorKey, type TypographyVariant } from '@scalewing/tokens';
|
|
2
2
|
import { type HTMLAttributes, type LabelHTMLAttributes } from 'react';
|
|
3
3
|
export type TextElement = 'p' | 'span' | 'h1' | 'h2' | 'h3' | 'h4' | 'label' | 'strong';
|
|
4
|
+
export type TextAlign = 'start' | 'center' | 'end';
|
|
4
5
|
export type TextProps = HTMLAttributes<HTMLElement> & Pick<LabelHTMLAttributes<HTMLLabelElement>, 'htmlFor'> & {
|
|
6
|
+
align?: TextAlign;
|
|
5
7
|
as?: TextElement;
|
|
6
8
|
color?: SemanticColorKey;
|
|
7
9
|
truncate?: boolean;
|
|
8
10
|
variant?: TypographyVariant;
|
|
9
11
|
};
|
|
10
12
|
export declare const Text: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLElement> & Pick<LabelHTMLAttributes<HTMLLabelElement>, "htmlFor"> & {
|
|
13
|
+
align?: TextAlign;
|
|
11
14
|
as?: TextElement;
|
|
12
15
|
color?: SemanticColorKey;
|
|
13
16
|
truncate?: boolean;
|
package/dist/components/Text.js
CHANGED
|
@@ -10,7 +10,7 @@ const defaultElement = {
|
|
|
10
10
|
label: 'span',
|
|
11
11
|
title: 'h3',
|
|
12
12
|
};
|
|
13
|
-
export const Text = forwardRef(function Text({ as, className, color = 'text', style, truncate = false, variant = 'body', ...rest }, ref) {
|
|
13
|
+
export const Text = forwardRef(function Text({ align, as, className, color = 'text', style, truncate = false, variant = 'body', ...rest }, ref) {
|
|
14
14
|
const Component = as ?? defaultElement[variant];
|
|
15
|
-
return (_jsx(Component, { ref: ref, className: cx(`sw-text-${variant}`, truncate && 'sw-truncate', className), style: { color: `var(--sw-color-${color})`, ...style }, ...rest }));
|
|
15
|
+
return (_jsx(Component, { ref: ref, className: cx(`sw-text-${variant}`, align && `sw-text-align-${align}`, truncate && 'sw-truncate', className), style: { color: `var(--sw-color-${color})`, ...style }, ...rest }));
|
|
16
16
|
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type DenominationGridProps } from './types.js';
|
|
2
|
+
type StripProps = Required<Pick<DenominationGridProps, 'label' | 'columns' | 'rows' | 'zeroLabel'>>;
|
|
3
|
+
export declare function DenominationStrip({ label, columns, rows, zeroLabel, }: StripProps): import("react").JSX.Element;
|
|
4
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from '../../class-names.js';
|
|
3
|
+
import { denominationCellView } from '../../denomination-cells.js';
|
|
4
|
+
export function DenominationStrip({ label, columns, rows, zeroLabel, }) {
|
|
5
|
+
const hasTotals = rows.some((row) => row.total !== undefined);
|
|
6
|
+
return (_jsxs("table", { className: "sw-denomination-grid sw-denomination-strip", children: [_jsx("caption", { className: "sw-sr-only", children: label }), _jsx("thead", { children: _jsxs("tr", { children: [_jsx("td", { className: "sw-denomination-corner" }), columns.map((column) => (_jsx("th", { className: "sw-denomination-head", scope: "col", children: column.label }, column.key))), hasTotals ? _jsx("td", { className: "sw-denomination-corner" }) : null] }) }), _jsx("tbody", { children: rows.map((row) => (_jsxs("tr", { className: cx('sw-denomination-row', `sw-denomination-row-${row.tone ?? 'neutral'}`, row.signed && 'sw-denomination-row-signed'), children: [_jsxs("th", { className: "sw-denomination-label", scope: "row", children: [row.icon ? (_jsx("span", { "aria-hidden": "true", className: "sw-denomination-icon", children: row.icon })) : null, _jsx("span", { className: "sw-denomination-label-text", children: row.label }), row.total !== undefined ? (_jsx("span", { "aria-hidden": "true", className: "sw-denomination-total-inline", children: row.total })) : null] }), columns.map((column, index) => {
|
|
7
|
+
const view = denominationCellView(row.cells[index] ?? null, row.signed ?? false, zeroLabel);
|
|
8
|
+
return (_jsx("td", { className: `sw-denomination-cell sw-denomination-cell-${view.state}`, children: view.text }, column.key));
|
|
9
|
+
}), hasTotals ? (_jsx("td", { className: "sw-denomination-total", children: row.total ?? '' })) : null] }, row.id))) })] }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type DenominationGridProps } from './types.js';
|
|
2
|
+
type TilesProps = Required<Pick<DenominationGridProps, 'label' | 'columns' | 'rows' | 'zeroLabel'>> & Pick<DenominationGridProps, 'subtotal'>;
|
|
3
|
+
export declare function DenominationTiles({ label, columns, rows, subtotal, zeroLabel, }: TilesProps): import("react").JSX.Element;
|
|
4
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from '../../class-names.js';
|
|
3
|
+
import { denominationCellView } from '../../denomination-cells.js';
|
|
4
|
+
export function DenominationTiles({ label, columns, rows, subtotal, zeroLabel, }) {
|
|
5
|
+
return (_jsx("div", { "aria-label": label, className: "sw-denomination-grid sw-denomination-tiles", role: "group", children: rows.map((row) => (_jsxs("section", { "aria-label": row.label, className: cx('sw-denomination-row', `sw-denomination-row-${row.tone ?? 'neutral'}`), children: [rows.length > 1 || row.icon ? (_jsxs("span", { className: "sw-denomination-label", children: [row.icon ? (_jsx("span", { "aria-hidden": "true", className: "sw-denomination-icon", children: row.icon })) : null, _jsx("span", { className: "sw-denomination-label-text", children: row.label }), row.total !== undefined ? (_jsx("span", { className: "sw-denomination-total", children: row.total })) : null] })) : null, _jsx("ul", { className: "sw-denomination-tile-list", children: columns.map((column, index) => {
|
|
6
|
+
const cell = row.cells[index] ?? null;
|
|
7
|
+
const view = denominationCellView(cell, row.signed ?? false, zeroLabel);
|
|
8
|
+
const amount = subtotal && cell !== null && cell !== 0
|
|
9
|
+
? subtotal(cell, column)
|
|
10
|
+
: null;
|
|
11
|
+
return (_jsxs("li", { className: "sw-denomination-tile", children: [_jsx("span", { className: "sw-denomination-head", children: column.label }), _jsx("span", { className: `sw-denomination-cell sw-denomination-cell-${view.state}`, children: view.text }), subtotal ? (_jsx("span", { className: "sw-denomination-subtotal", children: amount ?? zeroLabel })) : null] }, column.key));
|
|
12
|
+
}) })] }, row.id))) }));
|
|
13
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type BadgeTone } from '../../css/css-data.js';
|
|
3
|
+
export type DenominationGridTone = BadgeTone;
|
|
4
|
+
export type DenominationGridLayout = 'strip' | 'tiles';
|
|
5
|
+
export type DenominationGridColumn = {
|
|
6
|
+
key: string;
|
|
7
|
+
label: string;
|
|
8
|
+
};
|
|
9
|
+
export type DenominationGridRow = {
|
|
10
|
+
id: string;
|
|
11
|
+
label: string;
|
|
12
|
+
cells: readonly (number | null)[];
|
|
13
|
+
icon?: ReactNode;
|
|
14
|
+
tone?: DenominationGridTone;
|
|
15
|
+
signed?: boolean;
|
|
16
|
+
total?: string;
|
|
17
|
+
};
|
|
18
|
+
export type DenominationGridProps = {
|
|
19
|
+
label: string;
|
|
20
|
+
columns: readonly DenominationGridColumn[];
|
|
21
|
+
rows: readonly DenominationGridRow[];
|
|
22
|
+
layout?: DenominationGridLayout;
|
|
23
|
+
subtotal?: (count: number, column: DenominationGridColumn) => string;
|
|
24
|
+
zeroLabel?: string;
|
|
25
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { isDenominationCount } from '../../denomination-cells.js';
|
|
2
|
+
export function assertDenominationGrid(label, columns, rows) {
|
|
3
|
+
if (!label.trim())
|
|
4
|
+
throw new RangeError('label must not be empty');
|
|
5
|
+
if (columns.length === 0 ||
|
|
6
|
+
columns.some((column) => !column.key.trim() || !column.label.trim()))
|
|
7
|
+
throw new RangeError('columns must have nonempty keys and labels');
|
|
8
|
+
if (new Set(columns.map((column) => column.key)).size !== columns.length)
|
|
9
|
+
throw new RangeError('columns must have unique keys');
|
|
10
|
+
if (rows.some((row) => !row.id.trim() || !row.label.trim()))
|
|
11
|
+
throw new RangeError('rows must have nonempty ids and labels');
|
|
12
|
+
if (new Set(rows.map((row) => row.id)).size !== rows.length)
|
|
13
|
+
throw new RangeError('rows must have unique ids');
|
|
14
|
+
if (rows.some((row) => row.cells.length !== columns.length))
|
|
15
|
+
throw new RangeError('each row needs one cell per column');
|
|
16
|
+
if (rows.some((row) => row.cells.some((cell) => !isDenominationCount(cell))))
|
|
17
|
+
throw new RangeError('cells must be integers or null');
|
|
18
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const breakpointScale: {
|
|
2
|
+
readonly md: 48;
|
|
3
|
+
};
|
|
4
|
+
export type Breakpoint = keyof typeof breakpointScale;
|
|
5
|
+
export declare const breakpoints: Breakpoint[];
|
|
6
|
+
export type HideDirection = 'from' | 'below';
|
|
7
|
+
export declare function hideClass(direction: HideDirection, breakpoint: Breakpoint): string;
|
|
8
|
+
export declare function breakpointQuery(direction: HideDirection, breakpoint: Breakpoint): string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const breakpointScale = {
|
|
2
|
+
md: 48,
|
|
3
|
+
};
|
|
4
|
+
export const breakpoints = Object.keys(breakpointScale);
|
|
5
|
+
export function hideClass(direction, breakpoint) {
|
|
6
|
+
return `sw-hide-${direction}-${breakpoint}`;
|
|
7
|
+
}
|
|
8
|
+
export function breakpointQuery(direction, breakpoint) {
|
|
9
|
+
const minWidth = `(min-width: ${breakpointScale[breakpoint]}rem)`;
|
|
10
|
+
return direction === 'from' ? minWidth : `not all and ${minWidth}`;
|
|
11
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function cssAccordionClasses() {
|
|
2
|
+
return `.sw-accordion {
|
|
3
|
+
background: var(--sw-glass-fill);
|
|
4
|
+
border: 1px solid var(--sw-glass-border);
|
|
5
|
+
border-radius: var(--sw-radius-lg);
|
|
6
|
+
box-shadow: inset 0 1px 0 var(--sw-glass-specular);
|
|
7
|
+
backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
|
|
8
|
+
-webkit-backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
|
|
9
|
+
color: var(--sw-color-text);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.sw-accordion-summary {
|
|
13
|
+
cursor: pointer;
|
|
14
|
+
display: list-item;
|
|
15
|
+
min-height: var(--sw-control-md-min-height);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.sw-accordion-summary::-webkit-details-marker {
|
|
19
|
+
color: var(--sw-color-muted);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.sw-accordion-summary::marker {
|
|
23
|
+
color: var(--sw-color-muted);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.sw-accordion-summary:focus-visible {
|
|
27
|
+
outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
|
|
28
|
+
outline-offset: var(--sw-focus-ring-offset);
|
|
29
|
+
}`;
|
|
30
|
+
}
|
|
31
|
+
export function accordionClassCatalog() {
|
|
32
|
+
return ['sw-accordion', 'sw-accordion-summary'];
|
|
33
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { typographyVariants } from '@scalewing/tokens';
|
|
2
|
+
const label = typographyVariants.label;
|
|
3
|
+
export function cssActionMenuClasses() {
|
|
4
|
+
return `.sw-action-menu {
|
|
5
|
+
display: inline-flex;
|
|
6
|
+
position: relative;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.sw-action-menu-trigger {
|
|
10
|
+
appearance: none;
|
|
11
|
+
background: var(--sw-glass-fill);
|
|
12
|
+
border: 1px solid var(--sw-color-border);
|
|
13
|
+
border-radius: var(--sw-radius-sm);
|
|
14
|
+
color: var(--sw-color-text);
|
|
15
|
+
cursor: pointer;
|
|
16
|
+
font: inherit;
|
|
17
|
+
min-height: var(--sw-control-xs-min-height);
|
|
18
|
+
padding-inline: var(--sw-control-xs-padding-inline);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.sw-action-menu-trigger:focus-visible,
|
|
22
|
+
.sw-action-menu-item:focus-visible {
|
|
23
|
+
outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
|
|
24
|
+
outline-offset: var(--sw-focus-ring-offset);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.sw-action-menu-trigger:disabled {
|
|
28
|
+
cursor: not-allowed;
|
|
29
|
+
opacity: 0.5;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.sw-action-menu-list {
|
|
33
|
+
background: var(--sw-glass-fill);
|
|
34
|
+
border: 1px solid var(--sw-glass-border);
|
|
35
|
+
border-radius: var(--sw-radius-sm);
|
|
36
|
+
box-shadow: var(--sw-elevation-sm), inset 0 1px 0 var(--sw-glass-specular);
|
|
37
|
+
backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
|
|
38
|
+
-webkit-backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
|
|
39
|
+
box-sizing: border-box;
|
|
40
|
+
color: var(--sw-color-text);
|
|
41
|
+
margin: 0;
|
|
42
|
+
max-height: var(--sw-select-max);
|
|
43
|
+
max-width: calc(100vw - var(--sw-space-2));
|
|
44
|
+
min-width: max-content;
|
|
45
|
+
overflow: auto;
|
|
46
|
+
padding: var(--sw-space-1);
|
|
47
|
+
position: fixed;
|
|
48
|
+
z-index: 10;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.sw-action-menu-list[hidden] {
|
|
52
|
+
display: none;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.sw-action-menu-item {
|
|
56
|
+
align-items: center;
|
|
57
|
+
appearance: none;
|
|
58
|
+
background: transparent;
|
|
59
|
+
border: 0;
|
|
60
|
+
border-radius: var(--sw-radius-sm);
|
|
61
|
+
color: inherit;
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
display: flex;
|
|
64
|
+
font-family: var(--sw-font-sans);
|
|
65
|
+
font-size: ${label.fontSize}px;
|
|
66
|
+
gap: var(--sw-space-2);
|
|
67
|
+
min-height: var(--sw-control-xs-min-height);
|
|
68
|
+
padding-inline: var(--sw-control-xs-padding-inline);
|
|
69
|
+
text-align: start;
|
|
70
|
+
width: 100%;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.sw-action-menu-item:hover,
|
|
74
|
+
.sw-action-menu-item:focus-visible {
|
|
75
|
+
background: color-mix(in srgb, var(--sw-color-muted) 16%, transparent);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.sw-action-menu-item:disabled {
|
|
79
|
+
cursor: not-allowed;
|
|
80
|
+
opacity: 0.5;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.sw-action-menu-item-danger {
|
|
84
|
+
color: var(--sw-color-danger);
|
|
85
|
+
}`;
|
|
86
|
+
}
|
|
87
|
+
export function actionMenuClassCatalog() {
|
|
88
|
+
return [
|
|
89
|
+
'sw-action-menu',
|
|
90
|
+
'sw-action-menu-trigger',
|
|
91
|
+
'sw-action-menu-list',
|
|
92
|
+
'sw-action-menu-item',
|
|
93
|
+
'sw-action-menu-item-danger',
|
|
94
|
+
];
|
|
95
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ButtonSize, type ButtonVariant } from '@scalewing/tokens';
|
|
2
|
+
export declare function buttonClassNames(options: {
|
|
3
|
+
size: ButtonSize;
|
|
4
|
+
variant: ButtonVariant;
|
|
5
|
+
}): string[];
|
|
6
|
+
export declare function cssButtonClasses(): string;
|
|
7
|
+
export declare function buttonClassCatalog(): string[];
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { buttonSizes, buttonVariants, controlScale, typographyVariants, } from '@scalewing/tokens';
|
|
2
|
+
const label = typographyVariants.label;
|
|
3
|
+
export function buttonClassNames(options) {
|
|
4
|
+
return [
|
|
5
|
+
'sw-button',
|
|
6
|
+
`sw-button-${options.variant}`,
|
|
7
|
+
`sw-button-${options.size}`,
|
|
8
|
+
];
|
|
9
|
+
}
|
|
10
|
+
function variantRules() {
|
|
11
|
+
return [
|
|
12
|
+
`.sw-button-primary { background: var(--sw-color-accent); border-color: transparent; color: var(--sw-color-onAccent); }`,
|
|
13
|
+
`.sw-button-secondary { background: var(--sw-color-surface); border-color: var(--sw-color-border); color: var(--sw-color-text); }`,
|
|
14
|
+
`.sw-button-ghost { background: transparent; border-color: transparent; color: var(--sw-color-accent); }`,
|
|
15
|
+
`.sw-button-danger { background: var(--sw-color-danger); border-color: transparent; color: var(--sw-color-onDanger); }`,
|
|
16
|
+
].join('\n');
|
|
17
|
+
}
|
|
18
|
+
function sizeRules() {
|
|
19
|
+
return buttonSizes
|
|
20
|
+
.map((size) => {
|
|
21
|
+
const control = controlScale[size];
|
|
22
|
+
return `.sw-button-${size} { min-height: ${control.minHeight}px; padding-inline: ${control.paddingInline}px; }`;
|
|
23
|
+
})
|
|
24
|
+
.join('\n');
|
|
25
|
+
}
|
|
26
|
+
export function cssButtonClasses() {
|
|
27
|
+
return `.sw-button {
|
|
28
|
+
appearance: none;
|
|
29
|
+
align-items: center;
|
|
30
|
+
background: none;
|
|
31
|
+
border: 1px solid transparent;
|
|
32
|
+
border-radius: var(--sw-radius-pill);
|
|
33
|
+
box-sizing: border-box;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
display: inline-flex;
|
|
36
|
+
font-family: var(--sw-font-sans);
|
|
37
|
+
font-size: ${label.fontSize}px;
|
|
38
|
+
font-weight: ${label.fontWeight};
|
|
39
|
+
justify-content: center;
|
|
40
|
+
letter-spacing: ${label.letterSpacing}px;
|
|
41
|
+
line-height: ${label.lineHeight}px;
|
|
42
|
+
margin: 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.sw-button:focus-visible {
|
|
46
|
+
outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
|
|
47
|
+
outline-offset: var(--sw-focus-ring-offset);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.sw-button:disabled {
|
|
51
|
+
cursor: not-allowed;
|
|
52
|
+
opacity: var(--sw-disabled-opacity);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.sw-button[aria-pressed='false'] {
|
|
56
|
+
opacity: var(--sw-quiet-opacity);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
${variantRules()}
|
|
60
|
+
${sizeRules()}`;
|
|
61
|
+
}
|
|
62
|
+
export function buttonClassCatalog() {
|
|
63
|
+
return [
|
|
64
|
+
'sw-button',
|
|
65
|
+
...buttonVariants.map((variant) => `sw-button-${variant}`),
|
|
66
|
+
...buttonSizes.map((size) => `sw-button-${size}`),
|
|
67
|
+
];
|
|
68
|
+
}
|