@patternmode/swatch 3.0.0 → 4.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/README.md CHANGED
@@ -33,6 +33,34 @@ export function Example() {
33
33
 
34
34
  Use `color` for a solid fill, `background` for a CSS background value, or `colors` for weighted palette stops. `Swatch` remains representation-only: compose selection around it, or pass `onRemove` when the swatch should expose its built-in remove request affordance.
35
35
 
36
+ ## Transparent values
37
+
38
+ A partially transparent fill composites against whatever is behind it, so on a
39
+ white page a 40% colour renders as a pale solid — indistinguishable from a
40
+ lighter opaque colour, or from an empty swatch. Pass `transparencyBackdrop` to
41
+ put a chequerboard behind the fill so the transparency reads as transparency:
42
+
43
+ ```tsx
44
+ <Swatch aria-label="Overlay tint" color="rgb(49 92 75 / 40%)" transparencyBackdrop />
45
+ ```
46
+
47
+ **It is an explicit declaration, never inferred.** Alpha cannot be detected
48
+ across everything a swatch accepts — a gradient, a `color-mix()`, a CSS variable
49
+ or child media can all carry alpha the component cannot see — so a swatch that
50
+ guessed would be right sometimes and silently wrong the rest of the time. You
51
+ know whether your value is transparent; say so.
52
+
53
+ Three knobs, each read at the point of use, so setting them on any ancestor works:
54
+
55
+ | Custom property | Default | Effect |
56
+ | ------------------------------------- | ------------------- | --------------------- |
57
+ | `--patternmode-swatch-backdrop-size` | `8px` | Chequer square size |
58
+ | `--patternmode-swatch-backdrop-color` | `var(--border, …)` | Chequer colour |
59
+ | `--patternmode-swatch-backdrop-base` | `var(--card, #fff)` | The surface behind it |
60
+
61
+ The two colour defaults come from the theme, so the backdrop follows a dark mode
62
+ without any work from you.
63
+
36
64
  ## Rendering as a child element
37
65
 
38
66
  By default `Swatch` renders its own `<figure>` wrapper. Pass `asChild` to render
@@ -71,10 +99,96 @@ import Image from "next/image";
71
99
  </Swatch>;
72
100
  ```
73
101
 
74
- Use `DistributionBar` as a sibling primitive when a weighted visual distribution should be edited with draggable boundary handles.
102
+ ## Distributions: two components, and they are not interchangeable
103
+
104
+ Use `DistributionBar` when a human allocates the weights and must be able to
105
+ change them: it renders a `<fieldset>` with `role="slider"` boundary handles that
106
+ drag and answer arrow keys.
107
+
108
+ Use **`DistributionDisplay`** when the weights were computed — a bin a
109
+ calculation filled rather than a share someone assigned. It draws the same
110
+ contiguous track and legend and nothing else. Dragging the edge of a computed bin
111
+ just lies about what the number is, which is why the read-only one exists rather
112
+ than being the editor with its handles hidden.
113
+
114
+ ```tsx
115
+ import { DistributionDisplay } from "@patternmode/swatch";
116
+
117
+ <DistributionDisplay
118
+ aria-label="Colour distribution"
119
+ emptyLabel="unclassified"
120
+ emptyValue={12}
121
+ legend="summary"
122
+ segments={[
123
+ { id: "evergreen", color: "#315c4b", label: "Evergreen", value: 48 },
124
+ { id: "saffron", color: "#d9a441", label: "Saffron", value: 30 },
125
+ ]}
126
+ />;
127
+ ```
128
+
129
+ One bordered track with hairline boundaries, not a flex row of individually
130
+ rounded `Swatch` blocks — contiguity is the hard part, and separate blocks leak
131
+ each swatch's own radius and shadow as seams.
132
+
133
+ - `legend` — `"segments"` (default, one entry per segment), `"summary"`
134
+ (assigned vs unassigned percentages), or `false`.
135
+ - `emptyValue` / `emptyLabel` — unassigned weight, drawn as a muted remainder and
136
+ included in the derived percentages.
137
+ - `onSegmentSelect` + `selectedSegmentId` — makes each segment a button and rings
138
+ the selected one. Selection is not editing; the element only becomes a
139
+ `<fieldset>` when it becomes interactive.
140
+ - Height and corner radius come from `--patternmode-distribution-height` and
141
+ `--patternmode-distribution-radius`.
142
+
143
+ Distribution segment values are weights, not persisted percentages. Both
144
+ components render segment widths proportionally and their legends display derived
145
+ percentages.
146
+
147
+ For external segment controls, keep `segments` controlled and pass the next value
148
+ to `onChange`. `updateDistributionSegment` does not change distribution values.
149
+
150
+ ## Exports
151
+
152
+ Everything the package ships. If it is not here, it is not public.
153
+
154
+ ### Components
155
+
156
+ | | |
157
+ | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
158
+ | `Swatch` | Colour, gradient, image and palette swatch. `SwatchProps` is the union of `SwatchDefaultProps` (own `<figure>`) and `SwatchRenderProps` (`asChild`). |
159
+ | `DistributionBar` | The **editor** — draggable, keyboard-adjustable boundary handles. `DistributionBarProps`. |
160
+ | `DistributionDisplay` | The **read-only** strip. `DistributionDisplayProps`. |
161
+
162
+ ### Distribution helpers
163
+
164
+ Pure functions over a segment list, so custom controls do not duplicate the bar
165
+ math. All return a new array; none mutate.
166
+
167
+ | | |
168
+ | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
169
+ | `getDistributionTotal(segments)` | Sum of sanitised weights; invalid or negative values count as 0. |
170
+ | `getDistributionBoundaryPercent(segments, boundaryIndex)` | Percentage position of the boundary after `boundaryIndex`. |
171
+ | `moveDistributionBoundary(segments, boundaryIndex, deltaValue, minValue)` | Moves weight between two adjacent segments, preserving their sum and holding each side above `minValue`. |
172
+ | `updateDistributionSegment(segments, segmentId, update)` | Changes segment metadata (label, colour). Cannot change `value` — the type forbids it. |
173
+ | `removeDistributionSegment(segments, segmentId)` | Removes a segment and redistributes its weight proportionally across the rest. |
174
+
175
+ ### Swatch helpers
176
+
177
+ | | |
178
+ | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
179
+ | `getSwatchColorsBackground(colors, blend?)` | The CSS background a palette produces. `blend` is `"step"` (default, hard boundaries) or `"smooth"` (interpolated in OKLab). Returns `undefined` for an empty palette. |
180
+ | `getSwatchAtmosphereBackground(colors, options?)` | The soft layered-radial "atmosphere" fill — overlapping elliptical pools rather than a flat or linear ramp. `SwatchAtmosphereOptions`: `density` (0 diffuse → 1 dense, default 0.5) and `gravity` (-1 sinks → 1 rises, default 0). |
181
+ | `getSwatchSizeVariableStyle(size, variableName?)` | The inline style object setting `--patternmode-swatch-size` for a size token, for framing something Swatch does not render itself. |
182
+
183
+ ### Constants and types
75
184
 
76
- Distribution segment values are weights, not persisted percentages. The bar renders segment widths proportionally and its legend displays derived percentages.
185
+ `SWATCH_SIZES`, `SWATCH_SIZE_VALUES`, `SWATCH_SHAPES`, `SWATCH_TEXTURES` the
186
+ allowed token lists, with `SwatchSize`, `SwatchShape`, `SwatchTexture` derived
187
+ from them, plus `SwatchColorStop` and `SwatchSharedProps`.
77
188
 
78
- For external segment controls, keep `segments` controlled and pass the next value to `onChange`. The package also exports `moveDistributionBoundary`, `updateDistributionSegment`, and `removeDistributionSegment` so custom controls can move handles, change segment metadata, or remove a segment without duplicating the bar math. `updateDistributionSegment` does not change distribution values.
189
+ `DistributionSegment` and `DistributionSegmentUpdate` are the segment types.
190
+ `DistributionBarSegment` and `DistributionBarSegmentUpdate` are their former
191
+ names, kept as identical-shape aliases — prefer the neutral ones, since the
192
+ segment belongs to both components rather than to the editor.
79
193
 
80
194
  Import `@patternmode/swatch/styles.css` once in your app.
@@ -0,0 +1,53 @@
1
+ import type { WeightedColorSegment } from "@patternmode/system";
2
+ /**
3
+ * Weighted segment used by DistributionBar and DistributionDisplay. Extends the
4
+ * shared {@link WeightedColorSegment} with a required stable `id` for editing.
5
+ *
6
+ * Named for the shape rather than for either consumer. It used to be
7
+ * `DistributionBarSegment`, which typed the read-only `DistributionDisplay` on
8
+ * the *editor's* segment type — one of four independent signals telling readers
9
+ * that the read-only component was the editor, and part of why two consumers
10
+ * concluded the catalog had no read-only distribution at all.
11
+ */
12
+ export interface DistributionSegment extends WeightedColorSegment {
13
+ id: string;
14
+ }
15
+ /** Segment metadata update; weight changes happen through boundary movement. */
16
+ export type DistributionSegmentUpdate = Partial<Omit<DistributionSegment, "value">> & {
17
+ value?: never;
18
+ };
19
+ /**
20
+ * The former name for {@link DistributionSegment}, kept so the rename is not a
21
+ * breaking change. Identical shape. Prefer `DistributionSegment` — this name
22
+ * says the segment belongs to the editor, which is exactly the confusion the
23
+ * rename removes.
24
+ *
25
+ * Not tagged `@deprecated`: nothing is wrong with code that uses it, and the tag
26
+ * would fire the repo's `no-deprecated` rule on the barrels that must re-export
27
+ * it for the alias to reach consumers at all.
28
+ */
29
+ export type DistributionBarSegment = DistributionSegment;
30
+ /**
31
+ * The former name for {@link DistributionSegmentUpdate}, kept so the rename is
32
+ * not a breaking change. Identical shape. Prefer `DistributionSegmentUpdate`.
33
+ */
34
+ export type DistributionBarSegmentUpdate = DistributionSegmentUpdate;
35
+ /** A segment's share of `total`, rounded to whole percent. */
36
+ export declare const getDerivedDistributionPercentage: (value: number, total: number) => number;
37
+ /** Sums sanitized segment weights, treating invalid or negative values as 0. */
38
+ export declare const getDistributionTotal: (segments: DistributionSegment[]) => number;
39
+ /** Returns the percentage position of the boundary after `boundaryIndex`. */
40
+ export declare const getDistributionBoundaryPercent: (segments: DistributionSegment[], boundaryIndex: number) => number;
41
+ /**
42
+ * Moves the boundary between two adjacent segments while preserving their sum.
43
+ *
44
+ * `deltaValue` is applied to the left segment and subtracted from the right
45
+ * segment. `minValue` prevents either side of the pair from collapsing below a
46
+ * caller-defined minimum.
47
+ */
48
+ export declare const moveDistributionBoundary: (segments: DistributionSegment[], boundaryIndex: number, deltaValue: number, minValue: number) => DistributionSegment[];
49
+ /** Removes a segment and redistributes its weight proportionally to the rest. */
50
+ export declare const removeDistributionSegment: (segments: DistributionSegment[], segmentId: string) => DistributionSegment[];
51
+ /** Updates non-weight segment metadata such as label or color. */
52
+ export declare const updateDistributionSegment: (segments: DistributionSegment[], segmentId: string, update: DistributionSegmentUpdate) => DistributionSegment[];
53
+ //# sourceMappingURL=distribution-math.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"distribution-math.d.ts","sourceRoot":"","sources":["../../src/Distribution/distribution-math.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEhE;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB;IAC/D,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,gFAAgF;AAChF,MAAM,MAAM,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC,GAAG;IACpF,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAEzD;;;GAGG;AACH,MAAM,MAAM,4BAA4B,GAAG,yBAAyB,CAAC;AAErE,8DAA8D;AAC9D,eAAO,MAAM,gCAAgC,GAAI,OAAO,MAAM,EAAE,OAAO,MAAM,KAAG,MACb,CAAC;AAOpE,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,GAAI,UAAU,mBAAmB,EAAE,KAAG,MACI,CAAC;AAE5E,6EAA6E;AAC7E,eAAO,MAAM,8BAA8B,GACzC,UAAU,mBAAmB,EAAE,EAC/B,eAAe,MAAM,KACpB,MAUF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GACnC,UAAU,mBAAmB,EAAE,EAC/B,eAAe,MAAM,EACrB,YAAY,MAAM,EAClB,UAAU,MAAM,KACf,mBAAmB,EAyBrB,CAAC;AAEF,iFAAiF;AACjF,eAAO,MAAM,yBAAyB,GACpC,UAAU,mBAAmB,EAAE,EAC/B,WAAW,MAAM,KAChB,mBAAmB,EAmCrB,CAAC;AAEF,kEAAkE;AAClE,eAAO,MAAM,yBAAyB,GACpC,UAAU,mBAAmB,EAAE,EAC/B,WAAW,MAAM,EACjB,QAAQ,yBAAyB,KAChC,mBAAmB,EACuE,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { DistributionSegment } from "./distribution-math";
2
+ interface DistributionSegmentsProps {
3
+ emptyValue?: number;
4
+ onSegmentSelect?: (segment: DistributionSegment) => void;
5
+ segments: DistributionSegment[];
6
+ selectedSegmentId?: string;
7
+ total: number;
8
+ }
9
+ interface DistributionSegmentLegendProps {
10
+ emptyLabel?: string;
11
+ emptyValue?: number;
12
+ segments: DistributionSegment[];
13
+ total: number;
14
+ }
15
+ /** The coloured track: one element per segment, plus any unassigned remainder. */
16
+ export declare const DistributionSegments: ({ emptyValue, onSegmentSelect, segments, selectedSegmentId, total, }: DistributionSegmentsProps) => import("react").JSX.Element;
17
+ /** Swatch-and-label legend, one entry per segment. */
18
+ export declare const DistributionSegmentLegend: ({ emptyLabel, emptyValue, segments, total, }: DistributionSegmentLegendProps) => import("react").JSX.Element;
19
+ export {};
20
+ //# sourceMappingURL=distribution-parts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"distribution-parts.d.ts","sourceRoot":"","sources":["../../src/Distribution/distribution-parts.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAgB/D,UAAU,yBAAyB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACzD,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,8BAA8B;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,kFAAkF;AAClF,eAAO,MAAM,oBAAoB,GAAI,sEAMlC,yBAAyB,gCA8C3B,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,yBAAyB,GAAI,8CAKvC,8BAA8B,gCA6BhC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { type DistributionBarSegment, type DistributionBarSegmentUpdate, type DistributionSegment, type DistributionSegmentUpdate, getDerivedDistributionPercentage, getDistributionBoundaryPercent, getDistributionTotal, moveDistributionBoundary, removeDistributionSegment, updateDistributionSegment, } from "./distribution-math";
2
+ export { DistributionSegmentLegend, DistributionSegments } from "./distribution-parts";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Distribution/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,EACjC,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,gCAAgC,EAChC,8BAA8B,EAC9B,oBAAoB,EACpB,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -1,32 +1,5 @@
1
1
  import type { HTMLAttributes } from "react";
2
- import type { DistributionBarSegment } from "./distribution-bar-math";
3
- export interface DistributionDisplayProps extends Omit<HTMLAttributes<HTMLElement>, "role" | "onSelect"> {
4
- /** Label used in summary legends for assigned segment weight. */
5
- assignedLabel?: string;
6
- /** Label used when `emptyValue` contributes unassigned weight. */
7
- emptyLabel?: string;
8
- /**
9
- * Extra unassigned weight included in derived percentage calculations.
10
- *
11
- * Default `0`.
12
- */
13
- emptyValue?: number;
14
- /**
15
- * Legend style for the read-only display.
16
- *
17
- * Default `"segments"`.
18
- */
19
- legend?: "segments" | "summary" | false;
20
- /**
21
- * When provided, each segment renders as a button and selecting one
22
- * invokes this callback. Pair with `selectedSegmentId` to mark a segment
23
- * as selected (renders a ring). Read-only by default.
24
- */
25
- onSegmentSelect?: (segment: DistributionBarSegment) => void;
26
- segments: DistributionBarSegment[];
27
- /** Id of the selected segment — renders a ring on that segment. */
28
- selectedSegmentId?: string;
29
- }
2
+ import type { DistributionSegment } from "../Distribution/distribution-math";
30
3
  export interface DistributionBarProps extends Omit<HTMLAttributes<HTMLFieldSetElement>, "onChange"> {
31
4
  /**
32
5
  * Show the per-segment legend below the bar, or hide it.
@@ -41,9 +14,9 @@ export interface DistributionBarProps extends Omit<HTMLAttributes<HTMLFieldSetEl
41
14
  */
42
15
  minValue?: number;
43
16
  /** Receives the full next segment list after drag or keyboard boundary moves. */
44
- onChange?: (segments: DistributionBarSegment[]) => void;
17
+ onChange?: (segments: DistributionSegment[]) => void;
45
18
  /** Weighted segments; displayed percentages are derived from their total. */
46
- segments: DistributionBarSegment[];
19
+ segments: DistributionSegment[];
47
20
  /**
48
21
  * Keyboard adjustment amount for boundary handles.
49
22
  *
@@ -51,6 +24,5 @@ export interface DistributionBarProps extends Omit<HTMLAttributes<HTMLFieldSetEl
51
24
  */
52
25
  step?: number;
53
26
  }
54
- export declare const DistributionDisplay: ({ "aria-label": ariaLabel, assignedLabel, className, emptyLabel, emptyValue, legend, onSegmentSelect, segments, selectedSegmentId, ...props }: DistributionDisplayProps) => import("react").JSX.Element;
55
27
  export declare const DistributionBar: ({ "aria-label": ariaLabel, className, legend, minValue, onChange, segments, step, ...props }: DistributionBarProps) => import("react").JSX.Element;
56
28
  //# sourceMappingURL=distribution-bar-root.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"distribution-bar-root.d.ts","sourceRoot":"","sources":["../../src/DistributionBar/distribution-bar-root.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAiB,cAAc,EAAiB,MAAM,OAAO,CAAC;AAO1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAEtE,MAAM,WAAW,wBAAyB,SAAQ,IAAI,CACpD,cAAc,CAAC,WAAW,CAAC,EAC3B,MAAM,GAAG,UAAU,CACpB;IACC,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,KAAK,CAAC;IACxC;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,CAAC;IAC5D,QAAQ,EAAE,sBAAsB,EAAE,CAAC;IACnC,mEAAmE;IACnE,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAChD,cAAc,CAAC,mBAAmB,CAAC,EACnC,UAAU,CACX;IACC;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,GAAG,KAAK,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,sBAAsB,EAAE,KAAK,IAAI,CAAC;IACxD,6EAA6E;IAC7E,QAAQ,EAAE,sBAAsB,EAAE,CAAC;IACnC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAuND,eAAO,MAAM,mBAAmB,GAAI,+IAWjC,wBAAwB,gCAwD1B,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,8FAS7B,oBAAoB,gCA8FtB,CAAC"}
1
+ {"version":3,"file":"distribution-bar-root.d.ts","sourceRoot":"","sources":["../../src/DistributionBar/distribution-bar-root.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,OAAO,CAAC;AAO3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAM7E,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAChD,cAAc,CAAC,mBAAmB,CAAC,EACnC,UAAU,CACX;IACC;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,GAAG,KAAK,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,mBAAmB,EAAE,KAAK,IAAI,CAAC;IACrD,6EAA6E;IAC7E,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAqDD,eAAO,MAAM,eAAe,GAAI,8FAS7B,oBAAoB,gCA8FtB,CAAC"}
@@ -1,3 +1,2 @@
1
- export { type DistributionBarSegment, type DistributionBarSegmentUpdate, getDistributionBoundaryPercent, getDistributionTotal, moveDistributionBoundary, removeDistributionSegment, updateDistributionSegment, } from "./distribution-bar-math";
2
- export { DistributionBar, type DistributionBarProps, DistributionDisplay, type DistributionDisplayProps, } from "./distribution-bar-root";
1
+ export { DistributionBar, type DistributionBarProps } from "./distribution-bar-root";
3
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/DistributionBar/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,EACjC,8BAA8B,EAC9B,oBAAoB,EACpB,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,EACzB,mBAAmB,EACnB,KAAK,wBAAwB,GAC9B,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/DistributionBar/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,52 @@
1
+ import type { HTMLAttributes } from "react";
2
+ import type { DistributionSegment } from "../Distribution/distribution-math";
3
+ export interface DistributionDisplayProps extends Omit<HTMLAttributes<HTMLElement>, "role" | "onSelect"> {
4
+ /** Label used in summary legends for assigned segment weight. */
5
+ assignedLabel?: string;
6
+ /** Label used when `emptyValue` contributes unassigned weight. */
7
+ emptyLabel?: string;
8
+ /**
9
+ * Extra unassigned weight included in derived percentage calculations.
10
+ *
11
+ * Default `0`.
12
+ */
13
+ emptyValue?: number;
14
+ /**
15
+ * Legend style for the read-only display.
16
+ *
17
+ * Default `"segments"`.
18
+ */
19
+ legend?: "segments" | "summary" | false;
20
+ /**
21
+ * When provided, each segment renders as a button and selecting one
22
+ * invokes this callback. Pair with `selectedSegmentId` to mark a segment
23
+ * as selected (renders a ring). Read-only by default.
24
+ */
25
+ onSegmentSelect?: (segment: DistributionSegment) => void;
26
+ segments: DistributionSegment[];
27
+ /** Id of the selected segment — renders a ring on that segment. */
28
+ selectedSegmentId?: string;
29
+ }
30
+ /**
31
+ * A read-only proportional strip: one bordered track of contiguous weighted
32
+ * segments, with an optional legend.
33
+ *
34
+ * **Not an editor.** `DistributionBar` is the editor — it renders `role="slider"`
35
+ * handles and mutates its segments. This one draws them and nothing else, which
36
+ * is the right shape when the weights were computed rather than allocated by a
37
+ * human: dragging the edge of a bucket a computation filled just lies.
38
+ *
39
+ * It lives in its own directory for that reason. It used to be filed inside
40
+ * `DistributionBar/` and exported from the editor's barrel, and the result was
41
+ * that two separate consumers looked for a read-only distribution strip, did not
42
+ * find one, and reported the territory unserved.
43
+ *
44
+ * Contiguity is the point: one track with hairline boundaries, not a flex row of
45
+ * individually-rounded `Swatch` blocks, which leaks each swatch's own radius and
46
+ * shadow as seams.
47
+ *
48
+ * Pass `onSegmentSelect` to make each segment a button — selection is still not
49
+ * editing, so the element stays a `figure` unless it becomes interactive.
50
+ */
51
+ export declare const DistributionDisplay: ({ "aria-label": ariaLabel, assignedLabel, className, emptyLabel, emptyValue, legend, onSegmentSelect, segments, selectedSegmentId, ...props }: DistributionDisplayProps) => import("react").JSX.Element;
52
+ //# sourceMappingURL=distribution-display-root.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"distribution-display-root.d.ts","sourceRoot":"","sources":["../../src/DistributionDisplay/distribution-display-root.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAK5C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAM7E,MAAM,WAAW,wBAAyB,SAAQ,IAAI,CACpD,cAAc,CAAC,WAAW,CAAC,EAC3B,MAAM,GAAG,UAAU,CACpB;IACC,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,KAAK,CAAC;IACxC;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACzD,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,mEAAmE;IACnE,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAmDD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,mBAAmB,GAAI,+IAWjC,wBAAwB,gCAwD1B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { DistributionDisplay, type DistributionDisplayProps } from "./distribution-display-root";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/DistributionDisplay/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,6BAA6B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"swatch-atmosphere.d.ts","sourceRoot":"","sources":["../../src/Swatch/swatch-atmosphere.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAyCD,eAAO,MAAM,6BAA6B,GACxC,QAAQ,eAAe,EAAE,GAAG,SAAS,EACrC,UAAS,uBAA4B,KACpC,MAAM,GAAG,SA6BX,CAAC"}
1
+ {"version":3,"file":"swatch-atmosphere.d.ts","sourceRoot":"","sources":["../../src/Swatch/swatch-atmosphere.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA+CD,eAAO,MAAM,6BAA6B,GACxC,QAAQ,eAAe,EAAE,GAAG,SAAS,EACrC,UAAS,uBAA4B,KACpC,MAAM,GAAG,SA6BX,CAAC"}
@@ -1,3 +1,3 @@
1
1
  import type { SwatchProps } from "./swatch-types";
2
- export declare const Swatch: ({ "aria-label": ariaLabel, background, blend, children, className, color, colors, density, flat, gravity, icon: Icon, isLight, objectFit, objectPosition, onRemove, raised, removeLabel, render, selected, shape, showRing, size, style, texture, unavailable, ...props }: SwatchProps) => import("react").JSX.Element;
2
+ export declare const Swatch: ({ "aria-label": ariaLabel, background, blend, children, className, color, colors, density, flat, gravity, icon: Icon, isLight, objectFit, objectPosition, onRemove, raised, removeLabel, render, selected, shape, showRing, size, style, texture, transparencyBackdrop, unavailable, ...props }: SwatchProps) => import("react").JSX.Element;
3
3
  //# sourceMappingURL=swatch-root.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"swatch-root.d.ts","sourceRoot":"","sources":["../../src/Swatch/swatch-root.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AA6NlD,eAAO,MAAM,MAAM,GAAI,2QA2BpB,WAAW,gCAsGb,CAAC"}
1
+ {"version":3,"file":"swatch-root.d.ts","sourceRoot":"","sources":["../../src/Swatch/swatch-root.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAsOlD,eAAO,MAAM,MAAM,GAAI,iSA4BpB,WAAW,gCAwGb,CAAC"}
@@ -112,6 +112,21 @@ export interface SwatchSharedProps extends HTMLAttributes<HTMLElement> {
112
112
  * `density` and `gravity` to shape the pools.
113
113
  */
114
114
  texture?: SwatchTexture;
115
+ /**
116
+ * Renders a visible backdrop behind the fill so a partially transparent
117
+ * Visual Value reads as transparent rather than as white or as absence.
118
+ *
119
+ * Opt in explicitly. CSS alpha cannot be detected reliably across every
120
+ * accepted value — a gradient, a `color-mix()`, a CSS variable or child
121
+ * media can all carry alpha the component cannot see — so this is a
122
+ * declaration by the caller, not an inference.
123
+ *
124
+ * Tune it with `--patternmode-swatch-backdrop-size` and
125
+ * `--patternmode-swatch-backdrop-color`.
126
+ *
127
+ * Default `false`.
128
+ */
129
+ transparencyBackdrop?: boolean;
115
130
  /**
116
131
  * Marks the swatch as unavailable.
117
132
  *
@@ -1 +1 @@
1
- {"version":3,"file":"swatch-types.d.ts","sourceRoot":"","sources":["../../src/Swatch/swatch-types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"swatch-types.d.ts","sourceRoot":"","sources":["../../src/Swatch/swatch-types.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { DistributionBar, type DistributionBarProps, type DistributionBarSegment, type DistributionBarSegmentUpdate, DistributionDisplay, type DistributionDisplayProps, getDistributionBoundaryPercent, getDistributionTotal, getSwatchAtmosphereBackground, getSwatchColorsBackground, getSwatchSizeVariableStyle, moveDistributionBoundary, removeDistributionSegment, SWATCH_SHAPES, SWATCH_SIZE_VALUES, SWATCH_SIZES, SWATCH_TEXTURES, Swatch, type SwatchRenderProps, type SwatchAtmosphereOptions, type SwatchColorStop, type SwatchDefaultProps, type SwatchProps, type SwatchShape, type SwatchSharedProps, type SwatchSize, type SwatchTexture, updateDistributionSegment, } from "./swatch";
1
+ export { DistributionBar, type DistributionBarProps, type DistributionBarSegment, type DistributionBarSegmentUpdate, DistributionDisplay, type DistributionDisplayProps, type DistributionSegment, type DistributionSegmentUpdate, getDistributionBoundaryPercent, getDistributionTotal, getSwatchAtmosphereBackground, getSwatchColorsBackground, getSwatchSizeVariableStyle, moveDistributionBoundary, removeDistributionSegment, SWATCH_SHAPES, SWATCH_SIZE_VALUES, SWATCH_SIZES, SWATCH_TEXTURES, Swatch, type SwatchRenderProps, type SwatchAtmosphereOptions, type SwatchColorStop, type SwatchDefaultProps, type SwatchProps, type SwatchShape, type SwatchSharedProps, type SwatchSize, type SwatchTexture, updateDistributionSegment, } from "./swatch";
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,EACjC,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,8BAA8B,EAC9B,oBAAoB,EACpB,6BAA6B,EAC7B,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,yBAAyB,EACzB,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,MAAM,EACN,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,yBAAyB,GAC1B,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,EACjC,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,8BAA8B,EAC9B,oBAAoB,EACpB,6BAA6B,EAC7B,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,yBAAyB,EACzB,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,MAAM,EACN,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,yBAAyB,GAC1B,MAAM,UAAU,CAAC"}
package/dist/index.mjs CHANGED
@@ -1,12 +1,14 @@
1
1
  "use client";
2
2
  import { PATTERNMODE_SIZES, PATTERNMODE_SIZE_VALUES, getObjectSizingStyle, isLightColor, joinClassNames, sanitizeWeight } from "@patternmode/system";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
4
  import { LazyMotion, domMax, m } from "motion/react";
4
5
  import { isValidElement, useRef, useState } from "react";
5
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  import { hexToRgb, rgbToHex } from "@instruments/colorscope/convert";
7
7
  import "@base-ui/react/merge-props";
8
8
  import { useRender } from "@base-ui/react/use-render";
9
- //#region src/DistributionBar/distribution-bar-math.ts
9
+ //#region src/Distribution/distribution-math.ts
10
+ /** A segment's share of `total`, rounded to whole percent. */
11
+ const getDerivedDistributionPercentage = (value, total) => total > 0 ? Math.round(sanitizeWeight(value) / total * 100) : 0;
10
12
  const clamp$1 = (value, min, max) => Math.min(Math.max(value, min), max);
11
13
  const roundValue = (value) => Number(value.toFixed(1));
12
14
  /** Sums sanitized segment weights, treating invalid or negative values as 0. */
@@ -80,14 +82,8 @@ const updateDistributionSegment = (segments, segmentId, update) => segments.map(
80
82
  ...update
81
83
  } : segment);
82
84
  //#endregion
83
- //#region src/DistributionBar/distribution-bar-root.tsx
84
- const getDerivedDistributionPercentage = (value, total) => total > 0 ? Math.round(sanitizeWeight(value) / total * 100) : 0;
85
- const getDistributionDisplayTotal = (segments, emptyValue) => getDistributionTotal(segments) + sanitizeWeight(emptyValue);
86
- const getDistributionDisplayAccessibleLabel = (segments, emptyValue, emptyLabel, total) => {
87
- const segmentLabels = segments.map((segment) => `${segment.label ?? segment.id} ${getDerivedDistributionPercentage(segment.value, total)}%`);
88
- if (emptyValue > 0) segmentLabels.push(`${emptyLabel} ${getDerivedDistributionPercentage(emptyValue, total)}%`);
89
- return segmentLabels.join(", ");
90
- };
85
+ //#region src/Distribution/distribution-parts.tsx
86
+ /** The coloured track: one element per segment, plus any unassigned remainder. */
91
87
  const DistributionSegments = ({ emptyValue = 0, onSegmentSelect, segments, selectedSegmentId, total }) => /* @__PURE__ */ jsxs("div", {
92
88
  className: "patternmode-distribution-bar__segments",
93
89
  children: [segments.map((segment) => {
@@ -119,6 +115,7 @@ const DistributionSegments = ({ emptyValue = 0, onSegmentSelect, segments, selec
119
115
  style: { width: total > 0 ? `${sanitizeWeight(emptyValue) / total * 100}%` : "0%" }
120
116
  }) : null]
121
117
  });
118
+ /** Swatch-and-label legend, one entry per segment. */
122
119
  const DistributionSegmentLegend = ({ emptyLabel, emptyValue = 0, segments, total }) => /* @__PURE__ */ jsxs("div", {
123
120
  className: "patternmode-distribution-bar__legend",
124
121
  children: [segments.map((segment) => {
@@ -147,21 +144,8 @@ const DistributionSegmentLegend = ({ emptyLabel, emptyValue = 0, segments, total
147
144
  "%"
148
145
  ] }) : null]
149
146
  });
150
- const DistributionSummaryLegend = ({ assignedLabel, emptyLabel, emptyValue, total }) => {
151
- const emptyPercentage = getDerivedDistributionPercentage(emptyValue, total);
152
- return /* @__PURE__ */ jsxs("div", {
153
- className: "patternmode-distribution-bar__legend",
154
- children: [/* @__PURE__ */ jsxs("span", { children: [
155
- Math.max(0, 100 - emptyPercentage),
156
- "% ",
157
- assignedLabel
158
- ] }), emptyValue > 0 ? /* @__PURE__ */ jsxs("span", { children: [
159
- emptyPercentage,
160
- "% ",
161
- emptyLabel
162
- ] }) : null]
163
- });
164
- };
147
+ //#endregion
148
+ //#region src/DistributionBar/distribution-bar-root.tsx
165
149
  const DistributionBarHandle = ({ "aria-label": ariaLabel, "aria-valuenow": ariaValueNow, "aria-valuetext": ariaValueText, boundaryPercent, onDrag, onDragEnd, onDragStart, onKeyDown }) => /* @__PURE__ */ jsx(LazyMotion, {
166
150
  features: domMax,
167
151
  children: /* @__PURE__ */ jsx(m.button, {
@@ -191,49 +175,6 @@ const DistributionBarHandle = ({ "aria-label": ariaLabel, "aria-valuenow": ariaV
191
175
  type: "button"
192
176
  })
193
177
  });
194
- const DistributionDisplay = ({ "aria-label": ariaLabel, assignedLabel = "assigned", className, emptyLabel = "unassigned", emptyValue = 0, legend = "segments", onSegmentSelect, segments, selectedSegmentId, ...props }) => {
195
- const total = getDistributionDisplayTotal(segments, emptyValue);
196
- const interactive = Boolean(onSegmentSelect);
197
- const accessibleLabel = ariaLabel ?? getDistributionDisplayAccessibleLabel(segments, emptyValue, emptyLabel, total);
198
- const content = /* @__PURE__ */ jsxs(Fragment, { children: [
199
- /* @__PURE__ */ jsx("div", {
200
- className: "patternmode-distribution-bar__track",
201
- children: /* @__PURE__ */ jsx(DistributionSegments, {
202
- emptyValue,
203
- onSegmentSelect,
204
- segments,
205
- selectedSegmentId,
206
- total
207
- })
208
- }),
209
- legend === "segments" ? /* @__PURE__ */ jsx(DistributionSegmentLegend, {
210
- emptyLabel,
211
- emptyValue,
212
- segments,
213
- total
214
- }) : null,
215
- legend === "summary" ? /* @__PURE__ */ jsx(DistributionSummaryLegend, {
216
- assignedLabel,
217
- emptyLabel,
218
- emptyValue,
219
- total
220
- }) : null
221
- ] });
222
- const sharedClassName = joinClassNames("patternmode-distribution-display", className);
223
- return interactive ? /* @__PURE__ */ jsx("fieldset", {
224
- ...props,
225
- "aria-label": accessibleLabel,
226
- className: sharedClassName,
227
- "data-slot": "distribution-display",
228
- children: content
229
- }) : /* @__PURE__ */ jsx("figure", {
230
- ...props,
231
- "aria-label": accessibleLabel,
232
- className: sharedClassName,
233
- "data-slot": "distribution-display",
234
- children: content
235
- });
236
- };
237
178
  const DistributionBar = ({ "aria-label": ariaLabel, className, legend = "segments", minValue = 4, onChange, segments, step = 1, ...props }) => {
238
179
  const trackRef = useRef(null);
239
180
  const dragStartSegmentsRef = useRef(null);
@@ -311,6 +252,93 @@ const DistributionBar = ({ "aria-label": ariaLabel, className, legend = "segment
311
252
  });
312
253
  };
313
254
  //#endregion
255
+ //#region src/DistributionDisplay/distribution-display-root.tsx
256
+ const getDistributionDisplayTotal = (segments, emptyValue) => getDistributionTotal(segments) + sanitizeWeight(emptyValue);
257
+ const getDistributionDisplayAccessibleLabel = (segments, emptyValue, emptyLabel, total) => {
258
+ const segmentLabels = segments.map((segment) => `${segment.label ?? segment.id} ${getDerivedDistributionPercentage(segment.value, total)}%`);
259
+ if (emptyValue > 0) segmentLabels.push(`${emptyLabel} ${getDerivedDistributionPercentage(emptyValue, total)}%`);
260
+ return segmentLabels.join(", ");
261
+ };
262
+ const DistributionSummaryLegend = ({ assignedLabel, emptyLabel, emptyValue, total }) => {
263
+ const emptyPercentage = getDerivedDistributionPercentage(emptyValue, total);
264
+ return /* @__PURE__ */ jsxs("div", {
265
+ className: "patternmode-distribution-bar__legend",
266
+ children: [/* @__PURE__ */ jsxs("span", { children: [
267
+ Math.max(0, 100 - emptyPercentage),
268
+ "% ",
269
+ assignedLabel
270
+ ] }), emptyValue > 0 ? /* @__PURE__ */ jsxs("span", { children: [
271
+ emptyPercentage,
272
+ "% ",
273
+ emptyLabel
274
+ ] }) : null]
275
+ });
276
+ };
277
+ /**
278
+ * A read-only proportional strip: one bordered track of contiguous weighted
279
+ * segments, with an optional legend.
280
+ *
281
+ * **Not an editor.** `DistributionBar` is the editor — it renders `role="slider"`
282
+ * handles and mutates its segments. This one draws them and nothing else, which
283
+ * is the right shape when the weights were computed rather than allocated by a
284
+ * human: dragging the edge of a bucket a computation filled just lies.
285
+ *
286
+ * It lives in its own directory for that reason. It used to be filed inside
287
+ * `DistributionBar/` and exported from the editor's barrel, and the result was
288
+ * that two separate consumers looked for a read-only distribution strip, did not
289
+ * find one, and reported the territory unserved.
290
+ *
291
+ * Contiguity is the point: one track with hairline boundaries, not a flex row of
292
+ * individually-rounded `Swatch` blocks, which leaks each swatch's own radius and
293
+ * shadow as seams.
294
+ *
295
+ * Pass `onSegmentSelect` to make each segment a button — selection is still not
296
+ * editing, so the element stays a `figure` unless it becomes interactive.
297
+ */
298
+ const DistributionDisplay = ({ "aria-label": ariaLabel, assignedLabel = "assigned", className, emptyLabel = "unassigned", emptyValue = 0, legend = "segments", onSegmentSelect, segments, selectedSegmentId, ...props }) => {
299
+ const total = getDistributionDisplayTotal(segments, emptyValue);
300
+ const interactive = Boolean(onSegmentSelect);
301
+ const accessibleLabel = ariaLabel ?? getDistributionDisplayAccessibleLabel(segments, emptyValue, emptyLabel, total);
302
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
303
+ /* @__PURE__ */ jsx("div", {
304
+ className: "patternmode-distribution-bar__track",
305
+ children: /* @__PURE__ */ jsx(DistributionSegments, {
306
+ emptyValue,
307
+ onSegmentSelect,
308
+ segments,
309
+ selectedSegmentId,
310
+ total
311
+ })
312
+ }),
313
+ legend === "segments" ? /* @__PURE__ */ jsx(DistributionSegmentLegend, {
314
+ emptyLabel,
315
+ emptyValue,
316
+ segments,
317
+ total
318
+ }) : null,
319
+ legend === "summary" ? /* @__PURE__ */ jsx(DistributionSummaryLegend, {
320
+ assignedLabel,
321
+ emptyLabel,
322
+ emptyValue,
323
+ total
324
+ }) : null
325
+ ] });
326
+ const sharedClassName = joinClassNames("patternmode-distribution-display", className);
327
+ return interactive ? /* @__PURE__ */ jsx("fieldset", {
328
+ ...props,
329
+ "aria-label": accessibleLabel,
330
+ className: sharedClassName,
331
+ "data-slot": "distribution-display",
332
+ children: content
333
+ }) : /* @__PURE__ */ jsx("figure", {
334
+ ...props,
335
+ "aria-label": accessibleLabel,
336
+ className: sharedClassName,
337
+ "data-slot": "distribution-display",
338
+ children: content
339
+ });
340
+ };
341
+ //#endregion
314
342
  //#region src/Swatch/swatch-atmosphere.ts
315
343
  /**
316
344
  * Per-pool layout for the atmosphere fill:
@@ -377,7 +405,7 @@ const withAlpha = (color, alpha) => {
377
405
  const suffix = Math.round(clamp(alpha, 0, 255)).toString(16).padStart(2, "0");
378
406
  return `${rgbToHex(rgb.r, rgb.g, rgb.b)}${suffix}`;
379
407
  }
380
- return `color-mix(in srgb, ${color} ${Math.round(clamp(alpha, 0, 255) / 255 * 100)}%, transparent)`;
408
+ return `color-mix(in oklab, ${color} ${Math.round(clamp(alpha, 0, 255) / 255 * 100)}%, transparent)`;
381
409
  };
382
410
  const getSwatchAtmosphereBackground = (colors, options = {}) => {
383
411
  if (colors === void 0 || colors.length === 0) return;
@@ -463,7 +491,11 @@ const SWATCH_TEXTURES = ["atmosphere"];
463
491
  const getSwatchSizeVariableStyle = (size, variableName = "--patternmode-swatch-size") => ({ [variableName]: SWATCH_SIZE_VALUES[size] });
464
492
  //#endregion
465
493
  //#region src/Swatch/swatch-root.tsx
466
- const SwatchContent = ({ children, flat, Icon, mediaStyle, selected, unavailable }) => /* @__PURE__ */ jsxs(Fragment, { children: [
494
+ const SwatchContent = ({ children, flat, Icon, mediaStyle, selected, transparencyBackdrop, unavailable }) => /* @__PURE__ */ jsxs(Fragment, { children: [
495
+ transparencyBackdrop ? /* @__PURE__ */ jsx("span", {
496
+ "aria-hidden": "true",
497
+ className: "patternmode-swatch__backdrop"
498
+ }) : null,
467
499
  /* @__PURE__ */ jsx("span", {
468
500
  "aria-hidden": "true",
469
501
  className: "patternmode-swatch__fill"
@@ -505,7 +537,7 @@ const getSwatchTone = ({ background, color, colorsBackground, isLight }) => {
505
537
  if (color !== void 0 && color !== "" && !(background !== void 0 && background !== "") && !(colorsBackground !== void 0 && colorsBackground !== "") && isLightColor(color)) return "light";
506
538
  return "dark";
507
539
  };
508
- const getSwatchDataProps = ({ flat, lightTone, raised, selected, shape, showRing, size, unavailable }) => ({
540
+ const getSwatchDataProps = ({ flat, lightTone, raised, selected, shape, showRing, size, transparencyBackdrop, unavailable }) => ({
509
541
  "data-flat": flat ? "true" : void 0,
510
542
  "data-raised": raised ? "true" : void 0,
511
543
  "data-selected": selected ? "true" : void 0,
@@ -514,6 +546,7 @@ const getSwatchDataProps = ({ flat, lightTone, raised, selected, shape, showRing
514
546
  "data-size": size,
515
547
  "data-slot": "swatch",
516
548
  "data-tone": lightTone,
549
+ "data-transparency-backdrop": transparencyBackdrop ? "true" : void 0,
517
550
  "data-unavailable": unavailable ? "true" : void 0
518
551
  });
519
552
  const RemovableSwatch = ({ ariaLabel, children, className, dataProps, onRemove, removeLabel, rootStyle, props }) => {
@@ -558,7 +591,7 @@ const warnOnRenderChildren = (render) => {
558
591
  const { props } = render;
559
592
  if (typeof props === "object" && props !== null && "children" in props && props.children !== void 0 && props.children !== null) console.warn("Swatch `render` element has its own children, which override the swatch fill layers. Keep the `render` element childless and pass swatch content as `<Swatch>…</Swatch>` children.");
560
593
  };
561
- const Swatch = ({ "aria-label": ariaLabel, background, blend = "step", children, className, color, colors, density, flat = false, gravity, icon: Icon, isLight, objectFit, objectPosition, onRemove, raised = false, removeLabel, render, selected = false, shape = "circle", showRing = true, size = "base", style, texture, unavailable = false, ...props }) => {
594
+ const Swatch = ({ "aria-label": ariaLabel, background, blend = "step", children, className, color, colors, density, flat = false, gravity, icon: Icon, isLight, objectFit, objectPosition, onRemove, raised = false, removeLabel, render, selected = false, shape = "circle", showRing = true, size = "base", style, texture, transparencyBackdrop = false, unavailable = false, ...props }) => {
562
595
  const { colorsBackground, fill } = getSwatchFill({
563
596
  background,
564
597
  blend,
@@ -592,6 +625,7 @@ const Swatch = ({ "aria-label": ariaLabel, background, blend = "step", children,
592
625
  shape,
593
626
  showRing,
594
627
  size,
628
+ transparencyBackdrop,
595
629
  unavailable
596
630
  });
597
631
  const swatchContent = /* @__PURE__ */ jsx(SwatchContent, {
@@ -599,6 +633,7 @@ const Swatch = ({ "aria-label": ariaLabel, background, blend = "step", children,
599
633
  Icon,
600
634
  mediaStyle,
601
635
  selected,
636
+ transparencyBackdrop,
602
637
  unavailable,
603
638
  children: render ? void 0 : children
604
639
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["clamp"],"sources":["../src/DistributionBar/distribution-bar-math.ts","../src/DistributionBar/distribution-bar-root.tsx","../src/Swatch/swatch-atmosphere.ts","../src/Swatch/swatch-colors.ts","../src/Swatch/swatch-types.ts","../src/Swatch/swatch-root.tsx"],"sourcesContent":["import { sanitizeWeight } from \"@patternmode/system\";\nimport type { WeightedColorSegment } from \"@patternmode/system\";\n\n/**\n * Weighted segment used by DistributionBar and DistributionDisplay. Extends the\n * shared {@link WeightedColorSegment} with a required stable `id` for editing.\n */\nexport interface DistributionBarSegment extends WeightedColorSegment {\n id: string;\n}\n\n/** Segment metadata update; weight changes happen through boundary movement. */\nexport type DistributionBarSegmentUpdate = Partial<Omit<DistributionBarSegment, \"value\">> & {\n value?: never;\n};\n\nconst clamp = (value: number, min: number, max: number): number =>\n Math.min(Math.max(value, min), max);\n\nconst roundValue = (value: number): number => Number(value.toFixed(1));\n\n/** Sums sanitized segment weights, treating invalid or negative values as 0. */\nexport const getDistributionTotal = (segments: DistributionBarSegment[]): number =>\n segments.reduce((sum, segment) => sum + sanitizeWeight(segment.value), 0);\n\n/** Returns the percentage position of the boundary after `boundaryIndex`. */\nexport const getDistributionBoundaryPercent = (\n segments: DistributionBarSegment[],\n boundaryIndex: number,\n): number => {\n const total = getDistributionTotal(segments);\n if (total <= 0) {\n return 0;\n }\n\n const boundaryValue = segments\n .slice(0, boundaryIndex + 1)\n .reduce((sum, segment) => sum + sanitizeWeight(segment.value), 0);\n return roundValue((boundaryValue / total) * 100);\n};\n\n/**\n * Moves the boundary between two adjacent segments while preserving their sum.\n *\n * `deltaValue` is applied to the left segment and subtracted from the right\n * segment. `minValue` prevents either side of the pair from collapsing below a\n * caller-defined minimum.\n */\nexport const moveDistributionBoundary = (\n segments: DistributionBarSegment[],\n boundaryIndex: number,\n deltaValue: number,\n minValue: number,\n): DistributionBarSegment[] => {\n const left = segments[boundaryIndex];\n const right = segments[boundaryIndex + 1];\n if (!(left && right)) {\n return segments;\n }\n\n const pairTotal = sanitizeWeight(left.value) + sanitizeWeight(right.value);\n const clampedMin = Math.max(0, Math.min(minValue, pairTotal / 2));\n const nextLeft = clamp(\n sanitizeWeight(left.value) + deltaValue,\n clampedMin,\n pairTotal - clampedMin,\n );\n const nextRight = pairTotal - nextLeft;\n\n return segments.map((segment, index) => {\n if (index === boundaryIndex) {\n return { ...segment, value: roundValue(nextLeft) };\n }\n if (index === boundaryIndex + 1) {\n return { ...segment, value: roundValue(nextRight) };\n }\n return segment;\n });\n};\n\n/** Removes a segment and redistributes its weight proportionally to the rest. */\nexport const removeDistributionSegment = (\n segments: DistributionBarSegment[],\n segmentId: string,\n): DistributionBarSegment[] => {\n if (segments.length <= 1) {\n return segments;\n }\n\n const removed = segments.find((segment) => segment.id === segmentId);\n if (!removed) {\n return segments;\n }\n\n const remaining = segments.filter((segment) => segment.id !== segmentId);\n const removedValue = sanitizeWeight(removed.value);\n const remainingTotal = getDistributionTotal(remaining);\n if (remainingTotal <= 0) {\n const equalValue = removedValue / remaining.length;\n return remaining.map((segment) => ({\n ...segment,\n value: roundValue(equalValue),\n }));\n }\n\n let assignedValue = 0;\n const originalTotal = getDistributionTotal(segments);\n return remaining.map((segment, index) => {\n if (index === remaining.length - 1) {\n return { ...segment, value: roundValue(originalTotal - assignedValue) };\n }\n\n const nextValue = roundValue(\n sanitizeWeight(segment.value) +\n (removedValue * sanitizeWeight(segment.value)) / remainingTotal,\n );\n assignedValue += nextValue;\n return { ...segment, value: nextValue };\n });\n};\n\n/** Updates non-weight segment metadata such as label or color. */\nexport const updateDistributionSegment = (\n segments: DistributionBarSegment[],\n segmentId: string,\n update: DistributionBarSegmentUpdate,\n): DistributionBarSegment[] =>\n segments.map((segment) => (segment.id === segmentId ? { ...segment, ...update } : segment));\n","import { joinClassNames, sanitizeWeight } from \"@patternmode/system\";\nimport { domMax, LazyMotion, m } from \"motion/react\";\nimport type { PanInfo } from \"motion/react\";\nimport type { CSSProperties, HTMLAttributes, KeyboardEvent } from \"react\";\nimport { useRef, useState } from \"react\";\nimport {\n getDistributionBoundaryPercent,\n getDistributionTotal,\n moveDistributionBoundary,\n} from \"./distribution-bar-math\";\nimport type { DistributionBarSegment } from \"./distribution-bar-math\";\n\nexport interface DistributionDisplayProps extends Omit<\n HTMLAttributes<HTMLElement>,\n \"role\" | \"onSelect\"\n> {\n /** Label used in summary legends for assigned segment weight. */\n assignedLabel?: string;\n /** Label used when `emptyValue` contributes unassigned weight. */\n emptyLabel?: string;\n /**\n * Extra unassigned weight included in derived percentage calculations.\n *\n * Default `0`.\n */\n emptyValue?: number;\n /**\n * Legend style for the read-only display.\n *\n * Default `\"segments\"`.\n */\n legend?: \"segments\" | \"summary\" | false;\n /**\n * When provided, each segment renders as a button and selecting one\n * invokes this callback. Pair with `selectedSegmentId` to mark a segment\n * as selected (renders a ring). Read-only by default.\n */\n onSegmentSelect?: (segment: DistributionBarSegment) => void;\n segments: DistributionBarSegment[];\n /** Id of the selected segment — renders a ring on that segment. */\n selectedSegmentId?: string;\n}\n\nexport interface DistributionBarProps extends Omit<\n HTMLAttributes<HTMLFieldSetElement>,\n \"onChange\"\n> {\n /**\n * Show the per-segment legend below the bar, or hide it.\n *\n * Default `\"segments\"`.\n */\n legend?: \"segments\" | false;\n /**\n * Minimum weight each side of a dragged boundary must retain.\n *\n * Default `4`.\n */\n minValue?: number;\n /** Receives the full next segment list after drag or keyboard boundary moves. */\n onChange?: (segments: DistributionBarSegment[]) => void;\n /** Weighted segments; displayed percentages are derived from their total. */\n segments: DistributionBarSegment[];\n /**\n * Keyboard adjustment amount for boundary handles.\n *\n * Default `1`.\n */\n step?: number;\n}\n\ninterface DistributionSegmentsProps {\n emptyValue?: number;\n onSegmentSelect?: (segment: DistributionBarSegment) => void;\n segments: DistributionBarSegment[];\n selectedSegmentId?: string;\n total: number;\n}\n\ninterface DistributionSegmentLegendProps {\n emptyLabel?: string;\n emptyValue?: number;\n segments: DistributionBarSegment[];\n total: number;\n}\n\ninterface DistributionSummaryLegendProps {\n assignedLabel: string;\n emptyLabel: string;\n emptyValue: number;\n total: number;\n}\n\ninterface DistributionBarHandleProps {\n \"aria-label\": string;\n \"aria-valuenow\": number;\n \"aria-valuetext\": string;\n boundaryPercent: number;\n onDrag: (info: PanInfo) => void;\n onDragEnd: (info: PanInfo) => void;\n onDragStart: () => void;\n onKeyDown: (event: KeyboardEvent<HTMLButtonElement>) => void;\n}\n\ntype DistributionSegmentStyle = CSSProperties &\n Partial<Record<`--${string}`, number | string | undefined>>;\n\nconst getDerivedDistributionPercentage = (value: number, total: number): number =>\n total > 0 ? Math.round((sanitizeWeight(value) / total) * 100) : 0;\n\nconst getDistributionDisplayTotal = (\n segments: DistributionBarSegment[],\n emptyValue: number,\n): number => getDistributionTotal(segments) + sanitizeWeight(emptyValue);\n\nconst getDistributionDisplayAccessibleLabel = (\n segments: DistributionBarSegment[],\n emptyValue: number,\n emptyLabel: string,\n total: number,\n): string => {\n const segmentLabels = segments.map(\n (segment) =>\n `${segment.label ?? segment.id} ${getDerivedDistributionPercentage(segment.value, total)}%`,\n );\n if (emptyValue > 0) {\n segmentLabels.push(`${emptyLabel} ${getDerivedDistributionPercentage(emptyValue, total)}%`);\n }\n\n return segmentLabels.join(\", \");\n};\n\nconst DistributionSegments = ({\n emptyValue = 0,\n onSegmentSelect,\n segments,\n selectedSegmentId,\n total,\n}: DistributionSegmentsProps) => (\n <div className=\"patternmode-distribution-bar__segments\">\n {segments.map((segment) => {\n const segmentStyle = {\n \"--patternmode-distribution-segment-color\": segment.color,\n width: total > 0 ? `${(sanitizeWeight(segment.value) / total) * 100}%` : \"0%\",\n } satisfies DistributionSegmentStyle;\n const isSelected = selectedSegmentId === segment.id;\n\n if (onSegmentSelect) {\n return (\n <button\n aria-label={`${segment.label ?? segment.id} ${getDerivedDistributionPercentage(segment.value, total)}%`}\n aria-pressed={isSelected}\n className=\"patternmode-distribution-bar__segment\"\n data-selected={isSelected ? \"true\" : undefined}\n key={segment.id}\n onClick={() => {\n onSegmentSelect(segment);\n }}\n style={segmentStyle}\n type=\"button\"\n />\n );\n }\n\n return (\n <div\n aria-hidden=\"true\"\n className=\"patternmode-distribution-bar__segment\"\n data-selected={isSelected ? \"true\" : undefined}\n key={segment.id}\n style={segmentStyle}\n />\n );\n })}\n {emptyValue > 0 ? (\n <div\n aria-hidden=\"true\"\n className=\"patternmode-distribution-bar__segment patternmode-distribution-bar__segment--empty\"\n style={{\n width: total > 0 ? `${(sanitizeWeight(emptyValue) / total) * 100}%` : \"0%\",\n }}\n />\n ) : null}\n </div>\n);\n\nconst DistributionSegmentLegend = ({\n emptyLabel,\n emptyValue = 0,\n segments,\n total,\n}: DistributionSegmentLegendProps) => (\n <div className=\"patternmode-distribution-bar__legend\">\n {segments.map((segment) => {\n const segmentStyle = {\n \"--patternmode-distribution-segment-color\": segment.color,\n backgroundColor: undefined,\n } satisfies DistributionSegmentStyle;\n\n return (\n <span key={segment.id}>\n <span\n aria-hidden=\"true\"\n className=\"patternmode-distribution-bar__swatch\"\n style={segmentStyle}\n />\n {segment.label ?? segment.id} {getDerivedDistributionPercentage(segment.value, total)}%\n </span>\n );\n })}\n {emptyValue > 0 && emptyLabel !== undefined && emptyLabel !== \"\" ? (\n <span>\n <span\n aria-hidden=\"true\"\n className=\"patternmode-distribution-bar__swatch patternmode-distribution-bar__swatch--empty\"\n />\n {emptyLabel} {getDerivedDistributionPercentage(emptyValue, total)}%\n </span>\n ) : null}\n </div>\n);\n\nconst DistributionSummaryLegend = ({\n assignedLabel,\n emptyLabel,\n emptyValue,\n total,\n}: DistributionSummaryLegendProps) => {\n const emptyPercentage = getDerivedDistributionPercentage(emptyValue, total);\n\n return (\n <div className=\"patternmode-distribution-bar__legend\">\n <span>\n {Math.max(0, 100 - emptyPercentage)}% {assignedLabel}\n </span>\n {emptyValue > 0 ? (\n <span>\n {emptyPercentage}% {emptyLabel}\n </span>\n ) : null}\n </div>\n );\n};\n\nconst DistributionBarHandle = ({\n \"aria-label\": ariaLabel,\n \"aria-valuenow\": ariaValueNow,\n \"aria-valuetext\": ariaValueText,\n boundaryPercent,\n onDrag,\n onDragEnd,\n onDragStart,\n onKeyDown,\n}: DistributionBarHandleProps) => (\n <LazyMotion features={domMax}>\n <m.button\n aria-label={ariaLabel}\n aria-orientation=\"horizontal\"\n aria-valuemax={100}\n aria-valuemin={0}\n aria-valuenow={ariaValueNow}\n aria-valuetext={ariaValueText}\n className=\"patternmode-distribution-bar__handle\"\n drag=\"x\"\n dragElastic={0}\n dragMomentum={false}\n dragSnapToOrigin\n onDrag={(_event, info) => {\n onDrag(info);\n }}\n onDragEnd={(_event, info) => {\n onDragEnd(info);\n }}\n onDragStart={onDragStart}\n onKeyDown={onKeyDown}\n role=\"slider\"\n style={{ left: `calc(${boundaryPercent}% - 1.375rem)` }}\n tabIndex={0}\n transformTemplate={() => \"none\"}\n type=\"button\"\n />\n </LazyMotion>\n);\n\nexport const DistributionDisplay = ({\n \"aria-label\": ariaLabel,\n assignedLabel = \"assigned\",\n className,\n emptyLabel = \"unassigned\",\n emptyValue = 0,\n legend = \"segments\",\n onSegmentSelect,\n segments,\n selectedSegmentId,\n ...props\n}: DistributionDisplayProps) => {\n const total = getDistributionDisplayTotal(segments, emptyValue);\n const interactive = Boolean(onSegmentSelect);\n const accessibleLabel =\n ariaLabel ?? getDistributionDisplayAccessibleLabel(segments, emptyValue, emptyLabel, total);\n\n const content = (\n <>\n <div className=\"patternmode-distribution-bar__track\">\n <DistributionSegments\n emptyValue={emptyValue}\n onSegmentSelect={onSegmentSelect}\n segments={segments}\n selectedSegmentId={selectedSegmentId}\n total={total}\n />\n </div>\n {legend === \"segments\" ? (\n <DistributionSegmentLegend\n emptyLabel={emptyLabel}\n emptyValue={emptyValue}\n segments={segments}\n total={total}\n />\n ) : null}\n {legend === \"summary\" ? (\n <DistributionSummaryLegend\n assignedLabel={assignedLabel}\n emptyLabel={emptyLabel}\n emptyValue={emptyValue}\n total={total}\n />\n ) : null}\n </>\n );\n const sharedClassName = joinClassNames(\"patternmode-distribution-display\", className);\n\n return interactive ? (\n <fieldset\n {...props}\n aria-label={accessibleLabel}\n className={sharedClassName}\n data-slot=\"distribution-display\"\n >\n {content}\n </fieldset>\n ) : (\n <figure\n {...props}\n aria-label={accessibleLabel}\n className={sharedClassName}\n data-slot=\"distribution-display\"\n >\n {content}\n </figure>\n );\n};\n\nexport const DistributionBar = ({\n \"aria-label\": ariaLabel,\n className,\n legend = \"segments\",\n minValue = 4,\n onChange,\n segments,\n step = 1,\n ...props\n}: DistributionBarProps) => {\n const trackRef = useRef<HTMLDivElement>(null);\n const dragStartSegmentsRef = useRef<DistributionBarSegment[] | null>(null);\n const [dragging, setDragging] = useState(false);\n const total = getDistributionTotal(segments);\n\n const moveBoundary = (boundaryIndex: number, deltaValue: number, sourceSegments = segments) => {\n onChange?.(moveDistributionBoundary(sourceSegments, boundaryIndex, deltaValue, minValue));\n };\n\n const handleDragStart = () => {\n dragStartSegmentsRef.current = segments;\n setDragging(true);\n };\n\n const handleDrag = (boundaryIndex: number, info: PanInfo) => {\n const sourceSegments = dragStartSegmentsRef.current ?? segments;\n const sourceTotal = getDistributionTotal(sourceSegments);\n const trackWidth = trackRef.current?.getBoundingClientRect().width ?? 0;\n if (!(trackWidth > 0 && sourceTotal > 0)) {\n return;\n }\n\n moveBoundary(boundaryIndex, (info.offset.x / trackWidth) * sourceTotal, sourceSegments);\n };\n\n const handleDragEnd = (boundaryIndex: number, info: PanInfo) => {\n handleDrag(boundaryIndex, info);\n dragStartSegmentsRef.current = null;\n setDragging(false);\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLButtonElement>, boundaryIndex: number) => {\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n moveBoundary(boundaryIndex, -step);\n }\n if (event.key === \"ArrowRight\") {\n event.preventDefault();\n moveBoundary(boundaryIndex, step);\n }\n };\n\n return (\n <fieldset\n {...props}\n aria-label={ariaLabel}\n className={joinClassNames(\"patternmode-distribution-bar\", className)}\n data-dragging={dragging ? \"true\" : undefined}\n data-slot=\"distribution-bar\"\n >\n <div className=\"patternmode-distribution-bar__track\" ref={trackRef}>\n <DistributionSegments segments={segments} total={total} />\n {segments.slice(0, -1).map((segment, boundaryIndex) => {\n const nextSegment = segments[boundaryIndex + 1];\n const boundaryPercent = getDistributionBoundaryPercent(segments, boundaryIndex);\n const label = `Adjust ${segment.label ?? segment.id} and ${\n nextSegment?.label ?? nextSegment?.id\n } distribution`;\n const leftValue = sanitizeWeight(segment.value);\n const rightValue = sanitizeWeight(nextSegment?.value ?? 0);\n const pairTotal = leftValue + rightValue;\n /* Slider value: the left segment's share of the adjacent pair, so\n arrow keys and drags read as moving weight between neighbours. */\n const leftShare = pairTotal > 0 ? Math.round((leftValue / pairTotal) * 100) : 0;\n const valueText = `${segment.label ?? segment.id} ${leftShare}%, ${\n nextSegment?.label ?? nextSegment?.id\n } ${100 - leftShare}%`;\n return (\n <DistributionBarHandle\n aria-label={label}\n aria-valuenow={leftShare}\n aria-valuetext={valueText}\n boundaryPercent={boundaryPercent}\n key={`${segment.id}-${nextSegment?.id ?? \"end\"}`}\n onDrag={(info) => {\n handleDrag(boundaryIndex, info);\n }}\n onDragEnd={(info) => {\n handleDragEnd(boundaryIndex, info);\n }}\n onDragStart={handleDragStart}\n onKeyDown={(event) => {\n handleKeyDown(event, boundaryIndex);\n }}\n />\n );\n })}\n </div>\n {legend === \"segments\" ? (\n <DistributionSegmentLegend segments={segments} total={total} />\n ) : null}\n </fieldset>\n );\n};\n","import { hexToRgb, rgbToHex } from \"@instruments/colorscope/convert\";\n\nimport type { SwatchColorStop } from \"./swatch-types\";\n\nexport interface SwatchAtmosphereOptions {\n /** 0 = diffuse, wide wash · 1 = dense, tight pools. Default 0.5. */\n density?: number;\n /** -1 = grounds (pools sink) · 1 = lifts (pools rise). Default 0. */\n gravity?: number;\n}\n\n/**\n * Per-pool layout for the atmosphere fill:\n * `[focal x%, focal y%, base alpha (0-255), radius delta %, gravity sign]`.\n *\n * The first three entries reproduce the original three-pool blend identity\n * gradient exactly; further entries extend the pattern for palettes with\n * more than three colors.\n */\nconst POOLS: readonly (readonly [number, number, number, number, number])[] = [\n [30, 42, 0xcc, 0, -1],\n [72, 58, 0x99, -5, 1],\n [45, 65, 0x77, 8, -1],\n [62, 32, 0x66, 3, 1],\n [24, 72, 0x55, -3, -1],\n [80, 40, 0x44, 6, 1],\n];\n\n/**\n * Build a soft, layered radial \"atmosphere\" background from color stops — a\n * stack of overlapping elliptical pools rather than a flat or linear fill.\n * Density controls how far each pool reaches; gravity shifts the pools\n * vertically. Returns `undefined` when there are no colors.\n */\n\nconst clamp = (value: number, min: number, max: number): number =>\n Math.min(max, Math.max(min, value));\n\nconst withAlpha = (color: string, alpha: number): string => {\n const rgb = hexToRgb(color);\n if (rgb !== null) {\n const suffix = Math.round(clamp(alpha, 0, 255))\n .toString(16)\n .padStart(2, \"0\");\n return `${rgbToHex(rgb.r, rgb.g, rgb.b)}${suffix}`;\n }\n const percent = Math.round((clamp(alpha, 0, 255) / 255) * 100);\n return `color-mix(in srgb, ${color} ${percent}%, transparent)`;\n};\n\nexport const getSwatchAtmosphereBackground = (\n colors: SwatchColorStop[] | undefined,\n options: SwatchAtmosphereOptions = {},\n): string | undefined => {\n if (colors === undefined || colors.length === 0) {\n return undefined;\n }\n\n const density = clamp(options.density ?? 0.5, 0, 1);\n const gravity = clamp(options.gravity ?? 0, -1, 1);\n const gy = Math.round(gravity * 8);\n const reach = Math.round(50 + (1 - density) * 15);\n\n const layers = colors.map((stop, index) => {\n const color = typeof stop === \"string\" ? stop : stop.color;\n const pool = POOLS[index % POOLS.length];\n if (pool === undefined) {\n throw new Error(\"Expected atmosphere pool to exist.\");\n }\n const [x, y, baseAlpha, radiusDelta, gravitySign] = pool;\n // Each wrap past the palette length fades the extra pools further back.\n const cycle = Math.floor(index / POOLS.length);\n const alpha = Math.max(0x22, baseAlpha - cycle * 0x22);\n const focalY = clamp(y + gravitySign * gy, 0, 100);\n const radius = Math.max(8, reach + radiusDelta);\n return `radial-gradient(ellipse at ${x}% ${focalY}%, ${withAlpha(\n color,\n alpha,\n )} 0%, transparent ${radius}%)`;\n });\n\n return layers.join(\", \");\n};\n","import { sanitizeWeight } from \"@patternmode/system\";\n\nimport type { SwatchColorStop } from \"./swatch-types\";\n\nconst toColorStop = (stop: SwatchColorStop): { color: string; ratio?: number } =>\n typeof stop === \"string\" ? { color: stop } : stop;\n\n/** Missing ratios default to equal weight; finite/negative handling is shared. */\nconst getRatioWeight = (ratio: number | undefined): number =>\n ratio === undefined ? 1 : sanitizeWeight(ratio);\n\nconst formatPercent = (value: number): string =>\n `${Number.isInteger(value) ? value : Number(value.toFixed(2))}%`;\n\nexport const getSwatchColorsBackground = (\n colors: SwatchColorStop[] | undefined,\n blend: \"smooth\" | \"step\" = \"step\",\n): string | undefined => {\n if (colors === undefined || colors.length === 0) {\n return undefined;\n }\n\n const [singleColor] = colors;\n if (colors.length === 1 && singleColor !== undefined) {\n return toColorStop(singleColor).color;\n }\n\n const stops = colors.map(toColorStop);\n\n /* Smooth blend: one position per stop, interpolated in OKLab so ramps\n read as a continuous region of color rather than discrete bands. Each\n stop sits at the cumulative midpoint of its ratio share (a 90/10 palette\n centers at 45% and 95%), so weighted palettes still read proportionally.\n Equal, missing, or all-zero ratios fall back to even spacing. */\n if (blend === \"smooth\") {\n const weights = stops.map((stop) => getRatioWeight(stop.ratio));\n const weightTotal = weights.reduce((sum, weight) => sum + weight, 0);\n const useEvenSpacing = weightTotal <= 0 || weights.every((weight) => weight === weights[0]);\n let cursor = 0;\n const parts = stops.map((stop, index) => {\n let position: number;\n if (useEvenSpacing) {\n position = stops.length === 1 ? 0 : (index / (stops.length - 1)) * 100;\n } else {\n const share = ((weights[index] ?? 0) / weightTotal) * 100;\n position = cursor + share / 2;\n cursor += share;\n }\n return `${stop.color} ${formatPercent(position)}`;\n });\n return `linear-gradient(in oklab 90deg, ${parts.join(\", \")})`;\n }\n\n const weights = stops.map((stop) => getRatioWeight(stop.ratio));\n const rawTotal = weights.reduce((sum, ratio) => sum + ratio, 0);\n const useEqualWeights = rawTotal <= 0;\n const total = useEqualWeights ? stops.length : rawTotal;\n let cursor = 0;\n const parts = stops.map((stop, index) => {\n const ratio = useEqualWeights ? 1 : (weights[index] ?? 0);\n const start = cursor;\n const end = index === stops.length - 1 ? 100 : cursor + (ratio / total) * 100;\n cursor = end;\n return `${stop.color} ${formatPercent(start)} ${formatPercent(end)}`;\n });\n\n return `linear-gradient(90deg, ${parts.join(\", \")})`;\n};\n","import { PATTERNMODE_SIZE_VALUES, PATTERNMODE_SIZES } from \"@patternmode/system\";\nimport type { ObjectFit } from \"@patternmode/system\";\nimport type { ComponentType, HTMLAttributes, ReactNode, SVGProps } from \"react\";\n\nimport type { RenderProp } from \"../render\";\n\nexport const SWATCH_SIZES = [...PATTERNMODE_SIZES, \"4xl\", \"5xl\", \"6xl\", \"7xl\"] as const;\n\nexport const SWATCH_SIZE_VALUES = {\n ...PATTERNMODE_SIZE_VALUES,\n \"4xl\": \"4.5rem\",\n \"5xl\": \"5rem\",\n \"6xl\": \"5.5rem\",\n \"7xl\": \"6rem\",\n} as const satisfies Record<SwatchSize, string>;\n\nexport const SWATCH_SHAPES = [\"circle\", \"pill\", \"square\", \"block\"] as const;\n\nexport const SWATCH_TEXTURES = [\"atmosphere\"] as const;\n\nexport type SwatchSize = (typeof SWATCH_SIZES)[number];\nexport type SwatchShape = (typeof SWATCH_SHAPES)[number];\nexport type SwatchTexture = (typeof SWATCH_TEXTURES)[number];\nexport type SwatchColorStop = string | { color: string; ratio?: number };\ntype SwatchIcon = ComponentType<SVGProps<SVGSVGElement>>;\n\nexport const getSwatchSizeVariableStyle = (\n size: SwatchSize,\n variableName = \"--patternmode-swatch-size\",\n): Record<string, string> => ({\n [variableName]: SWATCH_SIZE_VALUES[size],\n});\n\n/**\n * Visual and behavioural props shared by every Swatch rendering mode. These\n * map deterministically to the fill, scrim, shape, and size treatment\n * regardless of whether the swatch renders its own wrapper or a `render`\n * element.\n */\nexport interface SwatchSharedProps extends HTMLAttributes<HTMLElement> {\n background?: string;\n color?: string;\n /**\n * How multiple `colors` blend: `\"step\"` renders hard bands, `\"smooth\"`\n * renders a continuous OKLab-interpolated ramp. Ignored by\n * `texture=\"atmosphere\"`.\n *\n * Default `\"step\"`.\n */\n blend?: \"smooth\" | \"step\";\n colors?: SwatchColorStop[];\n /**\n * Atmosphere density (0 = diffuse wash, 1 = dense pools). Only applies when\n * `texture=\"atmosphere\"`.\n *\n * Default `0.5`.\n */\n density?: number;\n /**\n * Render a precise, flat color block: no top-to-bottom scrim gradient and\n * no drop shadow. Use for data visualisation where the fill must read as\n * the exact color value.\n *\n * Default `false`.\n */\n flat?: boolean;\n /**\n * Atmosphere gravity (-1 = pools sink, 1 = pools rise). Only applies when\n * `texture=\"atmosphere\"`.\n *\n * Default `0`.\n */\n gravity?: number;\n icon?: SwatchIcon;\n isLight?: boolean;\n /**\n * Object-fit mode applied to media children through CSS variables.\n *\n * Default `\"cover\"`.\n */\n objectFit?: ObjectFit;\n /**\n * Object-position value applied to media children through CSS variables.\n *\n * Default `\"center\"`.\n */\n objectPosition?: string;\n raised?: boolean;\n /**\n * Shows selected state and optional icon overlay.\n *\n * Default `false`.\n */\n selected?: boolean;\n /**\n * Rendered swatch shape.\n *\n * Default `\"circle\"`.\n */\n shape?: SwatchShape;\n /**\n * Whether selected swatches render their ring treatment.\n *\n * Default `true`.\n */\n showRing?: boolean;\n /**\n * Size token used for the swatch dimensions.\n *\n * Default `\"base\"`.\n */\n size?: SwatchSize;\n /**\n * Render supplied colors as a soft, layered radial atmosphere — overlapping\n * color pools — instead of a ratio-encoded weighted palette. Pair with\n * `density` and `gravity` to shape the pools.\n */\n texture?: SwatchTexture;\n /**\n * Marks the swatch as unavailable.\n *\n * Default `false`.\n */\n unavailable?: boolean;\n}\n\n/**\n * Default Swatch props: the swatch renders its own wrapper element\n * (`<figure>`, or `<fieldset>` when `onRemove` is set).\n */\nexport interface SwatchDefaultProps extends SwatchSharedProps {\n render?: undefined;\n /**\n * Optional media rendered inside the swatch frame.\n *\n * Swatch does not optimize image elements itself. Next.js consumers can pass\n * their own `next/image` `Image` component here and use `objectFit` /\n * `objectPosition` to align it with the swatch shape.\n */\n children?: ReactNode;\n /** Renders a remove affordance and calls this after stopping propagation. */\n onRemove?: () => void;\n /** Accessible label for the remove affordance. Defaults to the swatch label. */\n removeLabel?: string;\n}\n\n/**\n * `render` Swatch props: the swatch merges its className, style (size/fill\n * CSS variables), data attributes, and other props onto the element passed via\n * `render`, rendering through it (Base UI render-prop pattern) instead of\n * emitting its own wrapper. Use this when the swatch must *be* an interactive\n * element, such as a `<button>` cell in a color matrix.\n *\n * The `render` element must be childless — put the swatch's content in\n * `children`, e.g. `<Swatch render={<button type=\"button\" />}>A1</Swatch>`.\n * Children on the `render` element itself would override the swatch's own fill\n * layers.\n *\n * `onRemove` is unsupported in this mode — its remove affordance cannot be\n * composed into an arbitrary rendered element. Wrap a default Swatch instead\n * when a remove control is required.\n */\nexport interface SwatchRenderProps extends SwatchSharedProps {\n render: RenderProp;\n children?: ReactNode;\n onRemove?: never;\n removeLabel?: never;\n}\n\nexport type SwatchProps = SwatchDefaultProps | SwatchRenderProps;\n","import { getObjectSizingStyle, isLightColor, joinClassNames } from \"@patternmode/system\";\nimport { isValidElement } from \"react\";\nimport type { CSSProperties, HTMLAttributes, MouseEvent, ReactNode } from \"react\";\n\nimport { useRender } from \"../render\";\nimport type { RenderProp } from \"../render\";\nimport { getSwatchAtmosphereBackground } from \"./swatch-atmosphere\";\nimport { getSwatchColorsBackground } from \"./swatch-colors\";\nimport { getSwatchSizeVariableStyle } from \"./swatch-types\";\nimport type { SwatchProps } from \"./swatch-types\";\n\ntype SwatchRootStyle = CSSProperties & Record<\"--patternmode-swatch-fill\", string | undefined>;\n\ninterface SwatchContentProps {\n children: SwatchProps[\"children\"];\n flat: boolean;\n Icon: SwatchProps[\"icon\"];\n mediaStyle: CSSProperties;\n selected: boolean;\n unavailable: boolean;\n}\n\ninterface RemovableSwatchProps {\n ariaLabel: string | undefined;\n children: ReactNode;\n className: string | undefined;\n dataProps: ReturnType<typeof getSwatchDataProps>;\n onRemove: () => void;\n removeLabel: string;\n rootStyle: SwatchRootStyle;\n props: HTMLAttributes<HTMLElement>;\n}\n\nconst SwatchContent = ({\n children,\n flat,\n Icon,\n mediaStyle,\n selected,\n unavailable,\n}: SwatchContentProps) => (\n <>\n <span aria-hidden=\"true\" className=\"patternmode-swatch__fill\" />\n {children !== undefined && children !== null ? (\n <span className=\"patternmode-swatch__media\" style={mediaStyle}>\n {children}\n </span>\n ) : null}\n {flat ? null : <span aria-hidden=\"true\" className=\"patternmode-swatch__scrim\" />}\n {selected && Icon ? (\n <span className=\"patternmode-swatch__icon\">\n <Icon aria-hidden=\"true\" focusable=\"false\" />\n </span>\n ) : null}\n {unavailable ? <span aria-hidden=\"true\" className=\"patternmode-swatch__slash\" /> : null}\n </>\n);\n\nconst getSwatchFill = ({\n background,\n blend,\n color,\n colors,\n density,\n gravity,\n texture,\n}: Pick<\n SwatchProps,\n \"background\" | \"blend\" | \"color\" | \"colors\" | \"density\" | \"gravity\" | \"texture\"\n>) => {\n const colorsBackground = getSwatchColorsBackground(colors, blend);\n const atmosphereBackground =\n texture === \"atmosphere\"\n ? getSwatchAtmosphereBackground(colors, { density, gravity })\n : undefined;\n\n return {\n colorsBackground,\n fill: background ?? atmosphereBackground ?? colorsBackground ?? color,\n };\n};\n\nconst getSwatchTone = ({\n background,\n color,\n colorsBackground,\n isLight,\n}: {\n background: SwatchProps[\"background\"];\n color: SwatchProps[\"color\"];\n colorsBackground: string | undefined;\n isLight: SwatchProps[\"isLight\"];\n}) => {\n if (isLight !== undefined) {\n return isLight ? \"light\" : \"dark\";\n }\n\n const hasColor = color !== undefined && color !== \"\";\n const hasBackground = background !== undefined && background !== \"\";\n const hasColorsBackground = colorsBackground !== undefined && colorsBackground !== \"\";\n\n if (hasColor && !hasBackground && !hasColorsBackground && isLightColor(color)) {\n return \"light\";\n }\n\n return \"dark\";\n};\n\nconst getSwatchDataProps = ({\n flat,\n lightTone,\n raised,\n selected,\n shape,\n showRing,\n size,\n unavailable,\n}: {\n flat: boolean;\n lightTone: \"dark\" | \"light\";\n raised: boolean;\n selected: boolean;\n shape: SwatchProps[\"shape\"];\n showRing: boolean;\n size: SwatchProps[\"size\"];\n unavailable: boolean;\n}) => ({\n \"data-flat\": flat ? \"true\" : undefined,\n \"data-raised\": raised ? \"true\" : undefined,\n \"data-selected\": selected ? \"true\" : undefined,\n \"data-shape\": shape,\n \"data-show-ring\": showRing ? \"true\" : \"false\",\n \"data-size\": size,\n \"data-slot\": \"swatch\",\n \"data-tone\": lightTone,\n \"data-unavailable\": unavailable ? \"true\" : undefined,\n});\n\nconst RemovableSwatch = ({\n ariaLabel,\n children,\n className,\n dataProps,\n onRemove,\n removeLabel,\n rootStyle,\n props,\n}: RemovableSwatchProps) => {\n const handleRemove = (event: MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation();\n onRemove();\n };\n\n return (\n <fieldset\n {...props}\n {...dataProps}\n aria-label={ariaLabel}\n className={joinClassNames(\"patternmode-swatch\", className)}\n style={rootStyle}\n >\n {children}\n <button\n aria-label={removeLabel}\n className=\"patternmode-swatch__remove\"\n onClick={handleRemove}\n type=\"button\"\n >\n <svg aria-hidden=\"true\" fill=\"none\" viewBox=\"0 0 20 20\">\n <path d=\"M5.5 5.5l9 9M14.5 5.5l-9 9\" />\n </svg>\n </button>\n </fieldset>\n );\n};\n\ninterface SwatchElementProps {\n ariaLabel: string | undefined;\n className: string | undefined;\n content: ReactNode;\n dataProps: ReturnType<typeof getSwatchDataProps>;\n props: HTMLAttributes<HTMLElement>;\n render: RenderProp | undefined;\n rootStyle: SwatchRootStyle;\n}\n\n// Renders the swatch through `render` (or a default `<figure>`) via Base UI's\n// `useRender`. Isolated into its own component so the `useRender` hook is\n// always called unconditionally, regardless of which branch `Swatch` takes.\nconst SwatchElement = ({\n ariaLabel,\n className,\n content,\n dataProps,\n props,\n render,\n rootStyle,\n}: SwatchElementProps) =>\n useRender({\n defaultTagName: \"figure\",\n props: {\n ...props,\n ...dataProps,\n \"aria-label\": ariaLabel,\n children: content,\n className: joinClassNames(\"patternmode-swatch\", className),\n style: rootStyle,\n },\n render,\n });\n\nconst warnOnRenderChildren = (render: RenderProp) => {\n if (!isValidElement(render)) {\n return;\n }\n const { props } = render;\n if (\n typeof props === \"object\" &&\n props !== null &&\n \"children\" in props &&\n props.children !== undefined &&\n props.children !== null\n ) {\n console.warn(\n \"Swatch `render` element has its own children, which override the swatch fill layers. \" +\n \"Keep the `render` element childless and pass swatch content as `<Swatch>…</Swatch>` children.\",\n );\n }\n};\n\nexport const Swatch = ({\n \"aria-label\": ariaLabel,\n background,\n blend = \"step\",\n children,\n className,\n color,\n colors,\n density,\n flat = false,\n gravity,\n icon: Icon,\n isLight,\n objectFit,\n objectPosition,\n onRemove,\n raised = false,\n removeLabel,\n render,\n selected = false,\n shape = \"circle\",\n showRing = true,\n size = \"base\",\n style,\n texture,\n unavailable = false,\n ...props\n}: SwatchProps) => {\n const { colorsBackground, fill } = getSwatchFill({\n background,\n blend,\n color,\n colors,\n density,\n gravity,\n texture,\n });\n const lightTone = getSwatchTone({\n background,\n color,\n colorsBackground,\n isLight,\n });\n const resolvedRemoveLabel =\n removeLabel ?? (ariaLabel !== undefined && ariaLabel !== \"\" ? `Remove ${ariaLabel}` : \"Remove\");\n\n const rootStyle: SwatchRootStyle = {\n ...getSwatchSizeVariableStyle(size),\n \"--patternmode-swatch-fill\": fill,\n ...style,\n };\n const mediaStyle: CSSProperties = getObjectSizingStyle({\n fit: objectFit,\n position: objectPosition,\n });\n const dataProps = getSwatchDataProps({\n flat,\n lightTone,\n raised,\n selected,\n shape,\n showRing,\n size,\n unavailable,\n });\n\n const swatchContent = (\n <SwatchContent\n flat={flat}\n Icon={Icon}\n mediaStyle={mediaStyle}\n selected={selected}\n unavailable={unavailable}\n >\n {render ? undefined : children}\n </SwatchContent>\n );\n\n if (render !== undefined) {\n warnOnRenderChildren(render);\n // In `render` mode the swatch fill layers and the consumer's content are\n // composed side by side inside the rendered element (matching the old\n // Slot + Slottable behaviour), so children render unwrapped rather than\n // inside the media frame.\n return (\n <SwatchElement\n ariaLabel={ariaLabel}\n className={className}\n content={\n <>\n {swatchContent}\n {children}\n </>\n }\n dataProps={dataProps}\n props={props}\n render={render}\n rootStyle={rootStyle}\n />\n );\n }\n\n if (onRemove !== undefined) {\n return (\n <RemovableSwatch\n ariaLabel={ariaLabel}\n className={className}\n dataProps={dataProps}\n onRemove={onRemove}\n props={props}\n removeLabel={resolvedRemoveLabel}\n rootStyle={rootStyle}\n >\n {swatchContent}\n </RemovableSwatch>\n );\n }\n\n return (\n <SwatchElement\n ariaLabel={ariaLabel}\n className={className}\n content={swatchContent}\n dataProps={dataProps}\n props={props}\n render={undefined}\n rootStyle={rootStyle}\n />\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["clamp"],"sources":["../src/Distribution/distribution-math.ts","../src/Distribution/distribution-parts.tsx","../src/DistributionBar/distribution-bar-root.tsx","../src/DistributionDisplay/distribution-display-root.tsx","../src/Swatch/swatch-atmosphere.ts","../src/Swatch/swatch-colors.ts","../src/Swatch/swatch-types.ts","../src/Swatch/swatch-root.tsx"],"sourcesContent":["import { sanitizeWeight } from \"@patternmode/system\";\nimport type { WeightedColorSegment } from \"@patternmode/system\";\n\n/**\n * Weighted segment used by DistributionBar and DistributionDisplay. Extends the\n * shared {@link WeightedColorSegment} with a required stable `id` for editing.\n *\n * Named for the shape rather than for either consumer. It used to be\n * `DistributionBarSegment`, which typed the read-only `DistributionDisplay` on\n * the *editor's* segment type — one of four independent signals telling readers\n * that the read-only component was the editor, and part of why two consumers\n * concluded the catalog had no read-only distribution at all.\n */\nexport interface DistributionSegment extends WeightedColorSegment {\n id: string;\n}\n\n/** Segment metadata update; weight changes happen through boundary movement. */\nexport type DistributionSegmentUpdate = Partial<Omit<DistributionSegment, \"value\">> & {\n value?: never;\n};\n\n/**\n * The former name for {@link DistributionSegment}, kept so the rename is not a\n * breaking change. Identical shape. Prefer `DistributionSegment` — this name\n * says the segment belongs to the editor, which is exactly the confusion the\n * rename removes.\n *\n * Not tagged `@deprecated`: nothing is wrong with code that uses it, and the tag\n * would fire the repo's `no-deprecated` rule on the barrels that must re-export\n * it for the alias to reach consumers at all.\n */\nexport type DistributionBarSegment = DistributionSegment;\n\n/**\n * The former name for {@link DistributionSegmentUpdate}, kept so the rename is\n * not a breaking change. Identical shape. Prefer `DistributionSegmentUpdate`.\n */\nexport type DistributionBarSegmentUpdate = DistributionSegmentUpdate;\n\n/** A segment's share of `total`, rounded to whole percent. */\nexport const getDerivedDistributionPercentage = (value: number, total: number): number =>\n total > 0 ? Math.round((sanitizeWeight(value) / total) * 100) : 0;\n\nconst clamp = (value: number, min: number, max: number): number =>\n Math.min(Math.max(value, min), max);\n\nconst roundValue = (value: number): number => Number(value.toFixed(1));\n\n/** Sums sanitized segment weights, treating invalid or negative values as 0. */\nexport const getDistributionTotal = (segments: DistributionSegment[]): number =>\n segments.reduce((sum, segment) => sum + sanitizeWeight(segment.value), 0);\n\n/** Returns the percentage position of the boundary after `boundaryIndex`. */\nexport const getDistributionBoundaryPercent = (\n segments: DistributionSegment[],\n boundaryIndex: number,\n): number => {\n const total = getDistributionTotal(segments);\n if (total <= 0) {\n return 0;\n }\n\n const boundaryValue = segments\n .slice(0, boundaryIndex + 1)\n .reduce((sum, segment) => sum + sanitizeWeight(segment.value), 0);\n return roundValue((boundaryValue / total) * 100);\n};\n\n/**\n * Moves the boundary between two adjacent segments while preserving their sum.\n *\n * `deltaValue` is applied to the left segment and subtracted from the right\n * segment. `minValue` prevents either side of the pair from collapsing below a\n * caller-defined minimum.\n */\nexport const moveDistributionBoundary = (\n segments: DistributionSegment[],\n boundaryIndex: number,\n deltaValue: number,\n minValue: number,\n): DistributionSegment[] => {\n const left = segments[boundaryIndex];\n const right = segments[boundaryIndex + 1];\n if (!(left && right)) {\n return segments;\n }\n\n const pairTotal = sanitizeWeight(left.value) + sanitizeWeight(right.value);\n const clampedMin = Math.max(0, Math.min(minValue, pairTotal / 2));\n const nextLeft = clamp(\n sanitizeWeight(left.value) + deltaValue,\n clampedMin,\n pairTotal - clampedMin,\n );\n const nextRight = pairTotal - nextLeft;\n\n return segments.map((segment, index) => {\n if (index === boundaryIndex) {\n return { ...segment, value: roundValue(nextLeft) };\n }\n if (index === boundaryIndex + 1) {\n return { ...segment, value: roundValue(nextRight) };\n }\n return segment;\n });\n};\n\n/** Removes a segment and redistributes its weight proportionally to the rest. */\nexport const removeDistributionSegment = (\n segments: DistributionSegment[],\n segmentId: string,\n): DistributionSegment[] => {\n if (segments.length <= 1) {\n return segments;\n }\n\n const removed = segments.find((segment) => segment.id === segmentId);\n if (!removed) {\n return segments;\n }\n\n const remaining = segments.filter((segment) => segment.id !== segmentId);\n const removedValue = sanitizeWeight(removed.value);\n const remainingTotal = getDistributionTotal(remaining);\n if (remainingTotal <= 0) {\n const equalValue = removedValue / remaining.length;\n return remaining.map((segment) => ({\n ...segment,\n value: roundValue(equalValue),\n }));\n }\n\n let assignedValue = 0;\n const originalTotal = getDistributionTotal(segments);\n return remaining.map((segment, index) => {\n if (index === remaining.length - 1) {\n return { ...segment, value: roundValue(originalTotal - assignedValue) };\n }\n\n const nextValue = roundValue(\n sanitizeWeight(segment.value) +\n (removedValue * sanitizeWeight(segment.value)) / remainingTotal,\n );\n assignedValue += nextValue;\n return { ...segment, value: nextValue };\n });\n};\n\n/** Updates non-weight segment metadata such as label or color. */\nexport const updateDistributionSegment = (\n segments: DistributionSegment[],\n segmentId: string,\n update: DistributionSegmentUpdate,\n): DistributionSegment[] =>\n segments.map((segment) => (segment.id === segmentId ? { ...segment, ...update } : segment));\n","import { sanitizeWeight } from \"@patternmode/system\";\nimport type { CSSProperties } from \"react\";\nimport { getDerivedDistributionPercentage } from \"./distribution-math\";\nimport type { DistributionSegment } from \"./distribution-math\";\n\n/**\n * The pieces `DistributionBar` (the editor) and `DistributionDisplay` (the\n * read-only sibling) both draw.\n *\n * They live here rather than inside either component so that neither owns the\n * other. The class names stay on the `patternmode-distribution-bar__*` prefix\n * because they are the published styling contract — consumers target them —\n * and renaming them would be a breaking change for a naming problem the module\n * boundary already solves.\n */\n\ntype DistributionSegmentStyle = CSSProperties &\n Partial<Record<`--${string}`, number | string | undefined>>;\n\ninterface DistributionSegmentsProps {\n emptyValue?: number;\n onSegmentSelect?: (segment: DistributionSegment) => void;\n segments: DistributionSegment[];\n selectedSegmentId?: string;\n total: number;\n}\n\ninterface DistributionSegmentLegendProps {\n emptyLabel?: string;\n emptyValue?: number;\n segments: DistributionSegment[];\n total: number;\n}\n\n/** The coloured track: one element per segment, plus any unassigned remainder. */\nexport const DistributionSegments = ({\n emptyValue = 0,\n onSegmentSelect,\n segments,\n selectedSegmentId,\n total,\n}: DistributionSegmentsProps) => (\n <div className=\"patternmode-distribution-bar__segments\">\n {segments.map((segment) => {\n const segmentStyle = {\n \"--patternmode-distribution-segment-color\": segment.color,\n width: total > 0 ? `${(sanitizeWeight(segment.value) / total) * 100}%` : \"0%\",\n } satisfies DistributionSegmentStyle;\n const isSelected = selectedSegmentId === segment.id;\n\n if (onSegmentSelect) {\n return (\n <button\n aria-label={`${segment.label ?? segment.id} ${getDerivedDistributionPercentage(segment.value, total)}%`}\n aria-pressed={isSelected}\n className=\"patternmode-distribution-bar__segment\"\n data-selected={isSelected ? \"true\" : undefined}\n key={segment.id}\n onClick={() => {\n onSegmentSelect(segment);\n }}\n style={segmentStyle}\n type=\"button\"\n />\n );\n }\n\n return (\n <div\n aria-hidden=\"true\"\n className=\"patternmode-distribution-bar__segment\"\n data-selected={isSelected ? \"true\" : undefined}\n key={segment.id}\n style={segmentStyle}\n />\n );\n })}\n {emptyValue > 0 ? (\n <div\n aria-hidden=\"true\"\n className=\"patternmode-distribution-bar__segment patternmode-distribution-bar__segment--empty\"\n style={{\n width: total > 0 ? `${(sanitizeWeight(emptyValue) / total) * 100}%` : \"0%\",\n }}\n />\n ) : null}\n </div>\n);\n\n/** Swatch-and-label legend, one entry per segment. */\nexport const DistributionSegmentLegend = ({\n emptyLabel,\n emptyValue = 0,\n segments,\n total,\n}: DistributionSegmentLegendProps) => (\n <div className=\"patternmode-distribution-bar__legend\">\n {segments.map((segment) => {\n const segmentStyle = {\n \"--patternmode-distribution-segment-color\": segment.color,\n backgroundColor: undefined,\n } satisfies DistributionSegmentStyle;\n\n return (\n <span key={segment.id}>\n <span\n aria-hidden=\"true\"\n className=\"patternmode-distribution-bar__swatch\"\n style={segmentStyle}\n />\n {segment.label ?? segment.id} {getDerivedDistributionPercentage(segment.value, total)}%\n </span>\n );\n })}\n {emptyValue > 0 && emptyLabel !== undefined && emptyLabel !== \"\" ? (\n <span>\n <span\n aria-hidden=\"true\"\n className=\"patternmode-distribution-bar__swatch patternmode-distribution-bar__swatch--empty\"\n />\n {emptyLabel} {getDerivedDistributionPercentage(emptyValue, total)}%\n </span>\n ) : null}\n </div>\n);\n","import { joinClassNames, sanitizeWeight } from \"@patternmode/system\";\nimport { domMax, LazyMotion, m } from \"motion/react\";\nimport type { PanInfo } from \"motion/react\";\nimport type { HTMLAttributes, KeyboardEvent } from \"react\";\nimport { useRef, useState } from \"react\";\nimport {\n getDistributionBoundaryPercent,\n getDistributionTotal,\n moveDistributionBoundary,\n} from \"../Distribution/distribution-math\";\nimport type { DistributionSegment } from \"../Distribution/distribution-math\";\nimport {\n DistributionSegmentLegend,\n DistributionSegments,\n} from \"../Distribution/distribution-parts\";\n\nexport interface DistributionBarProps extends Omit<\n HTMLAttributes<HTMLFieldSetElement>,\n \"onChange\"\n> {\n /**\n * Show the per-segment legend below the bar, or hide it.\n *\n * Default `\"segments\"`.\n */\n legend?: \"segments\" | false;\n /**\n * Minimum weight each side of a dragged boundary must retain.\n *\n * Default `4`.\n */\n minValue?: number;\n /** Receives the full next segment list after drag or keyboard boundary moves. */\n onChange?: (segments: DistributionSegment[]) => void;\n /** Weighted segments; displayed percentages are derived from their total. */\n segments: DistributionSegment[];\n /**\n * Keyboard adjustment amount for boundary handles.\n *\n * Default `1`.\n */\n step?: number;\n}\n\ninterface DistributionBarHandleProps {\n \"aria-label\": string;\n \"aria-valuenow\": number;\n \"aria-valuetext\": string;\n boundaryPercent: number;\n onDrag: (info: PanInfo) => void;\n onDragEnd: (info: PanInfo) => void;\n onDragStart: () => void;\n onKeyDown: (event: KeyboardEvent<HTMLButtonElement>) => void;\n}\n\nconst DistributionBarHandle = ({\n \"aria-label\": ariaLabel,\n \"aria-valuenow\": ariaValueNow,\n \"aria-valuetext\": ariaValueText,\n boundaryPercent,\n onDrag,\n onDragEnd,\n onDragStart,\n onKeyDown,\n}: DistributionBarHandleProps) => (\n <LazyMotion features={domMax}>\n <m.button\n aria-label={ariaLabel}\n aria-orientation=\"horizontal\"\n aria-valuemax={100}\n aria-valuemin={0}\n aria-valuenow={ariaValueNow}\n aria-valuetext={ariaValueText}\n className=\"patternmode-distribution-bar__handle\"\n drag=\"x\"\n dragElastic={0}\n dragMomentum={false}\n dragSnapToOrigin\n onDrag={(_event, info) => {\n onDrag(info);\n }}\n onDragEnd={(_event, info) => {\n onDragEnd(info);\n }}\n onDragStart={onDragStart}\n onKeyDown={onKeyDown}\n role=\"slider\"\n style={{ left: `calc(${boundaryPercent}% - 1.375rem)` }}\n tabIndex={0}\n transformTemplate={() => \"none\"}\n type=\"button\"\n />\n </LazyMotion>\n);\n\nexport const DistributionBar = ({\n \"aria-label\": ariaLabel,\n className,\n legend = \"segments\",\n minValue = 4,\n onChange,\n segments,\n step = 1,\n ...props\n}: DistributionBarProps) => {\n const trackRef = useRef<HTMLDivElement>(null);\n const dragStartSegmentsRef = useRef<DistributionSegment[] | null>(null);\n const [dragging, setDragging] = useState(false);\n const total = getDistributionTotal(segments);\n\n const moveBoundary = (boundaryIndex: number, deltaValue: number, sourceSegments = segments) => {\n onChange?.(moveDistributionBoundary(sourceSegments, boundaryIndex, deltaValue, minValue));\n };\n\n const handleDragStart = () => {\n dragStartSegmentsRef.current = segments;\n setDragging(true);\n };\n\n const handleDrag = (boundaryIndex: number, info: PanInfo) => {\n const sourceSegments = dragStartSegmentsRef.current ?? segments;\n const sourceTotal = getDistributionTotal(sourceSegments);\n const trackWidth = trackRef.current?.getBoundingClientRect().width ?? 0;\n if (!(trackWidth > 0 && sourceTotal > 0)) {\n return;\n }\n\n moveBoundary(boundaryIndex, (info.offset.x / trackWidth) * sourceTotal, sourceSegments);\n };\n\n const handleDragEnd = (boundaryIndex: number, info: PanInfo) => {\n handleDrag(boundaryIndex, info);\n dragStartSegmentsRef.current = null;\n setDragging(false);\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLButtonElement>, boundaryIndex: number) => {\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n moveBoundary(boundaryIndex, -step);\n }\n if (event.key === \"ArrowRight\") {\n event.preventDefault();\n moveBoundary(boundaryIndex, step);\n }\n };\n\n return (\n <fieldset\n {...props}\n aria-label={ariaLabel}\n className={joinClassNames(\"patternmode-distribution-bar\", className)}\n data-dragging={dragging ? \"true\" : undefined}\n data-slot=\"distribution-bar\"\n >\n <div className=\"patternmode-distribution-bar__track\" ref={trackRef}>\n <DistributionSegments segments={segments} total={total} />\n {segments.slice(0, -1).map((segment, boundaryIndex) => {\n const nextSegment = segments[boundaryIndex + 1];\n const boundaryPercent = getDistributionBoundaryPercent(segments, boundaryIndex);\n const label = `Adjust ${segment.label ?? segment.id} and ${\n nextSegment?.label ?? nextSegment?.id\n } distribution`;\n const leftValue = sanitizeWeight(segment.value);\n const rightValue = sanitizeWeight(nextSegment?.value ?? 0);\n const pairTotal = leftValue + rightValue;\n /* Slider value: the left segment's share of the adjacent pair, so\n arrow keys and drags read as moving weight between neighbours. */\n const leftShare = pairTotal > 0 ? Math.round((leftValue / pairTotal) * 100) : 0;\n const valueText = `${segment.label ?? segment.id} ${leftShare}%, ${\n nextSegment?.label ?? nextSegment?.id\n } ${100 - leftShare}%`;\n return (\n <DistributionBarHandle\n aria-label={label}\n aria-valuenow={leftShare}\n aria-valuetext={valueText}\n boundaryPercent={boundaryPercent}\n key={`${segment.id}-${nextSegment?.id ?? \"end\"}`}\n onDrag={(info) => {\n handleDrag(boundaryIndex, info);\n }}\n onDragEnd={(info) => {\n handleDragEnd(boundaryIndex, info);\n }}\n onDragStart={handleDragStart}\n onKeyDown={(event) => {\n handleKeyDown(event, boundaryIndex);\n }}\n />\n );\n })}\n </div>\n {legend === \"segments\" ? (\n <DistributionSegmentLegend segments={segments} total={total} />\n ) : null}\n </fieldset>\n );\n};\n","import { joinClassNames, sanitizeWeight } from \"@patternmode/system\";\nimport type { HTMLAttributes } from \"react\";\nimport {\n getDerivedDistributionPercentage,\n getDistributionTotal,\n} from \"../Distribution/distribution-math\";\nimport type { DistributionSegment } from \"../Distribution/distribution-math\";\nimport {\n DistributionSegmentLegend,\n DistributionSegments,\n} from \"../Distribution/distribution-parts\";\n\nexport interface DistributionDisplayProps extends Omit<\n HTMLAttributes<HTMLElement>,\n \"role\" | \"onSelect\"\n> {\n /** Label used in summary legends for assigned segment weight. */\n assignedLabel?: string;\n /** Label used when `emptyValue` contributes unassigned weight. */\n emptyLabel?: string;\n /**\n * Extra unassigned weight included in derived percentage calculations.\n *\n * Default `0`.\n */\n emptyValue?: number;\n /**\n * Legend style for the read-only display.\n *\n * Default `\"segments\"`.\n */\n legend?: \"segments\" | \"summary\" | false;\n /**\n * When provided, each segment renders as a button and selecting one\n * invokes this callback. Pair with `selectedSegmentId` to mark a segment\n * as selected (renders a ring). Read-only by default.\n */\n onSegmentSelect?: (segment: DistributionSegment) => void;\n segments: DistributionSegment[];\n /** Id of the selected segment — renders a ring on that segment. */\n selectedSegmentId?: string;\n}\n\ninterface DistributionSummaryLegendProps {\n assignedLabel: string;\n emptyLabel: string;\n emptyValue: number;\n total: number;\n}\n\nconst getDistributionDisplayTotal = (segments: DistributionSegment[], emptyValue: number): number =>\n getDistributionTotal(segments) + sanitizeWeight(emptyValue);\n\nconst getDistributionDisplayAccessibleLabel = (\n segments: DistributionSegment[],\n emptyValue: number,\n emptyLabel: string,\n total: number,\n): string => {\n const segmentLabels = segments.map(\n (segment) =>\n `${segment.label ?? segment.id} ${getDerivedDistributionPercentage(segment.value, total)}%`,\n );\n if (emptyValue > 0) {\n segmentLabels.push(`${emptyLabel} ${getDerivedDistributionPercentage(emptyValue, total)}%`);\n }\n\n return segmentLabels.join(\", \");\n};\n\nconst DistributionSummaryLegend = ({\n assignedLabel,\n emptyLabel,\n emptyValue,\n total,\n}: DistributionSummaryLegendProps) => {\n const emptyPercentage = getDerivedDistributionPercentage(emptyValue, total);\n\n return (\n <div className=\"patternmode-distribution-bar__legend\">\n <span>\n {Math.max(0, 100 - emptyPercentage)}% {assignedLabel}\n </span>\n {emptyValue > 0 ? (\n <span>\n {emptyPercentage}% {emptyLabel}\n </span>\n ) : null}\n </div>\n );\n};\n\n/**\n * A read-only proportional strip: one bordered track of contiguous weighted\n * segments, with an optional legend.\n *\n * **Not an editor.** `DistributionBar` is the editor — it renders `role=\"slider\"`\n * handles and mutates its segments. This one draws them and nothing else, which\n * is the right shape when the weights were computed rather than allocated by a\n * human: dragging the edge of a bucket a computation filled just lies.\n *\n * It lives in its own directory for that reason. It used to be filed inside\n * `DistributionBar/` and exported from the editor's barrel, and the result was\n * that two separate consumers looked for a read-only distribution strip, did not\n * find one, and reported the territory unserved.\n *\n * Contiguity is the point: one track with hairline boundaries, not a flex row of\n * individually-rounded `Swatch` blocks, which leaks each swatch's own radius and\n * shadow as seams.\n *\n * Pass `onSegmentSelect` to make each segment a button — selection is still not\n * editing, so the element stays a `figure` unless it becomes interactive.\n */\nexport const DistributionDisplay = ({\n \"aria-label\": ariaLabel,\n assignedLabel = \"assigned\",\n className,\n emptyLabel = \"unassigned\",\n emptyValue = 0,\n legend = \"segments\",\n onSegmentSelect,\n segments,\n selectedSegmentId,\n ...props\n}: DistributionDisplayProps) => {\n const total = getDistributionDisplayTotal(segments, emptyValue);\n const interactive = Boolean(onSegmentSelect);\n const accessibleLabel =\n ariaLabel ?? getDistributionDisplayAccessibleLabel(segments, emptyValue, emptyLabel, total);\n\n const content = (\n <>\n <div className=\"patternmode-distribution-bar__track\">\n <DistributionSegments\n emptyValue={emptyValue}\n onSegmentSelect={onSegmentSelect}\n segments={segments}\n selectedSegmentId={selectedSegmentId}\n total={total}\n />\n </div>\n {legend === \"segments\" ? (\n <DistributionSegmentLegend\n emptyLabel={emptyLabel}\n emptyValue={emptyValue}\n segments={segments}\n total={total}\n />\n ) : null}\n {legend === \"summary\" ? (\n <DistributionSummaryLegend\n assignedLabel={assignedLabel}\n emptyLabel={emptyLabel}\n emptyValue={emptyValue}\n total={total}\n />\n ) : null}\n </>\n );\n const sharedClassName = joinClassNames(\"patternmode-distribution-display\", className);\n\n return interactive ? (\n <fieldset\n {...props}\n aria-label={accessibleLabel}\n className={sharedClassName}\n data-slot=\"distribution-display\"\n >\n {content}\n </fieldset>\n ) : (\n <figure\n {...props}\n aria-label={accessibleLabel}\n className={sharedClassName}\n data-slot=\"distribution-display\"\n >\n {content}\n </figure>\n );\n};\n","import { hexToRgb, rgbToHex } from \"@instruments/colorscope/convert\";\n\nimport type { SwatchColorStop } from \"./swatch-types\";\n\nexport interface SwatchAtmosphereOptions {\n /** 0 = diffuse, wide wash · 1 = dense, tight pools. Default 0.5. */\n density?: number;\n /** -1 = grounds (pools sink) · 1 = lifts (pools rise). Default 0. */\n gravity?: number;\n}\n\n/**\n * Per-pool layout for the atmosphere fill:\n * `[focal x%, focal y%, base alpha (0-255), radius delta %, gravity sign]`.\n *\n * The first three entries reproduce the original three-pool blend identity\n * gradient exactly; further entries extend the pattern for palettes with\n * more than three colors.\n */\nconst POOLS: readonly (readonly [number, number, number, number, number])[] = [\n [30, 42, 0xcc, 0, -1],\n [72, 58, 0x99, -5, 1],\n [45, 65, 0x77, 8, -1],\n [62, 32, 0x66, 3, 1],\n [24, 72, 0x55, -3, -1],\n [80, 40, 0x44, 6, 1],\n];\n\n/**\n * Build a soft, layered radial \"atmosphere\" background from color stops — a\n * stack of overlapping elliptical pools rather than a flat or linear fill.\n * Density controls how far each pool reaches; gravity shifts the pools\n * vertically. Returns `undefined` when there are no colors.\n */\n\nconst clamp = (value: number, min: number, max: number): number =>\n Math.min(max, Math.max(min, value));\n\nconst withAlpha = (color: string, alpha: number): string => {\n const rgb = hexToRgb(color);\n if (rgb !== null) {\n const suffix = Math.round(clamp(alpha, 0, 255))\n .toString(16)\n .padStart(2, \"0\");\n return `${rgbToHex(rgb.r, rgb.g, rgb.b)}${suffix}`;\n }\n const percent = Math.round((clamp(alpha, 0, 255) / 255) * 100);\n /* Mix in oklab, not srgb. This branch takes every non-hex colour, including\n the `oklch()` values a wide-gamut consumer passes — and `in srgb` narrows\n those to the sRGB gamut on the way through, silently. Nothing throws and\n the swatch still renders; it just renders a duller colour than the one it\n was given. The hex branch above is already sRGB by construction, so this\n is the only path where the gamut is live. */\n return `color-mix(in oklab, ${color} ${percent}%, transparent)`;\n};\n\nexport const getSwatchAtmosphereBackground = (\n colors: SwatchColorStop[] | undefined,\n options: SwatchAtmosphereOptions = {},\n): string | undefined => {\n if (colors === undefined || colors.length === 0) {\n return undefined;\n }\n\n const density = clamp(options.density ?? 0.5, 0, 1);\n const gravity = clamp(options.gravity ?? 0, -1, 1);\n const gy = Math.round(gravity * 8);\n const reach = Math.round(50 + (1 - density) * 15);\n\n const layers = colors.map((stop, index) => {\n const color = typeof stop === \"string\" ? stop : stop.color;\n const pool = POOLS[index % POOLS.length];\n if (pool === undefined) {\n throw new Error(\"Expected atmosphere pool to exist.\");\n }\n const [x, y, baseAlpha, radiusDelta, gravitySign] = pool;\n // Each wrap past the palette length fades the extra pools further back.\n const cycle = Math.floor(index / POOLS.length);\n const alpha = Math.max(0x22, baseAlpha - cycle * 0x22);\n const focalY = clamp(y + gravitySign * gy, 0, 100);\n const radius = Math.max(8, reach + radiusDelta);\n return `radial-gradient(ellipse at ${x}% ${focalY}%, ${withAlpha(\n color,\n alpha,\n )} 0%, transparent ${radius}%)`;\n });\n\n return layers.join(\", \");\n};\n","import { sanitizeWeight } from \"@patternmode/system\";\n\nimport type { SwatchColorStop } from \"./swatch-types\";\n\nconst toColorStop = (stop: SwatchColorStop): { color: string; ratio?: number } =>\n typeof stop === \"string\" ? { color: stop } : stop;\n\n/** Missing ratios default to equal weight; finite/negative handling is shared. */\nconst getRatioWeight = (ratio: number | undefined): number =>\n ratio === undefined ? 1 : sanitizeWeight(ratio);\n\nconst formatPercent = (value: number): string =>\n `${Number.isInteger(value) ? value : Number(value.toFixed(2))}%`;\n\nexport const getSwatchColorsBackground = (\n colors: SwatchColorStop[] | undefined,\n blend: \"smooth\" | \"step\" = \"step\",\n): string | undefined => {\n if (colors === undefined || colors.length === 0) {\n return undefined;\n }\n\n const [singleColor] = colors;\n if (colors.length === 1 && singleColor !== undefined) {\n return toColorStop(singleColor).color;\n }\n\n const stops = colors.map(toColorStop);\n\n /* Smooth blend: one position per stop, interpolated in OKLab so ramps\n read as a continuous region of color rather than discrete bands. Each\n stop sits at the cumulative midpoint of its ratio share (a 90/10 palette\n centers at 45% and 95%), so weighted palettes still read proportionally.\n Equal, missing, or all-zero ratios fall back to even spacing. */\n if (blend === \"smooth\") {\n const weights = stops.map((stop) => getRatioWeight(stop.ratio));\n const weightTotal = weights.reduce((sum, weight) => sum + weight, 0);\n const useEvenSpacing = weightTotal <= 0 || weights.every((weight) => weight === weights[0]);\n let cursor = 0;\n const parts = stops.map((stop, index) => {\n let position: number;\n if (useEvenSpacing) {\n position = stops.length === 1 ? 0 : (index / (stops.length - 1)) * 100;\n } else {\n const share = ((weights[index] ?? 0) / weightTotal) * 100;\n position = cursor + share / 2;\n cursor += share;\n }\n return `${stop.color} ${formatPercent(position)}`;\n });\n return `linear-gradient(in oklab 90deg, ${parts.join(\", \")})`;\n }\n\n const weights = stops.map((stop) => getRatioWeight(stop.ratio));\n const rawTotal = weights.reduce((sum, ratio) => sum + ratio, 0);\n const useEqualWeights = rawTotal <= 0;\n const total = useEqualWeights ? stops.length : rawTotal;\n let cursor = 0;\n const parts = stops.map((stop, index) => {\n const ratio = useEqualWeights ? 1 : (weights[index] ?? 0);\n const start = cursor;\n const end = index === stops.length - 1 ? 100 : cursor + (ratio / total) * 100;\n cursor = end;\n return `${stop.color} ${formatPercent(start)} ${formatPercent(end)}`;\n });\n\n return `linear-gradient(90deg, ${parts.join(\", \")})`;\n};\n","import { PATTERNMODE_SIZE_VALUES, PATTERNMODE_SIZES } from \"@patternmode/system\";\nimport type { ObjectFit } from \"@patternmode/system\";\nimport type { ComponentType, HTMLAttributes, ReactNode, SVGProps } from \"react\";\n\nimport type { RenderProp } from \"../render\";\n\nexport const SWATCH_SIZES = [...PATTERNMODE_SIZES, \"4xl\", \"5xl\", \"6xl\", \"7xl\"] as const;\n\nexport const SWATCH_SIZE_VALUES = {\n ...PATTERNMODE_SIZE_VALUES,\n \"4xl\": \"4.5rem\",\n \"5xl\": \"5rem\",\n \"6xl\": \"5.5rem\",\n \"7xl\": \"6rem\",\n} as const satisfies Record<SwatchSize, string>;\n\nexport const SWATCH_SHAPES = [\"circle\", \"pill\", \"square\", \"block\"] as const;\n\nexport const SWATCH_TEXTURES = [\"atmosphere\"] as const;\n\nexport type SwatchSize = (typeof SWATCH_SIZES)[number];\nexport type SwatchShape = (typeof SWATCH_SHAPES)[number];\nexport type SwatchTexture = (typeof SWATCH_TEXTURES)[number];\nexport type SwatchColorStop = string | { color: string; ratio?: number };\ntype SwatchIcon = ComponentType<SVGProps<SVGSVGElement>>;\n\nexport const getSwatchSizeVariableStyle = (\n size: SwatchSize,\n variableName = \"--patternmode-swatch-size\",\n): Record<string, string> => ({\n [variableName]: SWATCH_SIZE_VALUES[size],\n});\n\n/**\n * Visual and behavioural props shared by every Swatch rendering mode. These\n * map deterministically to the fill, scrim, shape, and size treatment\n * regardless of whether the swatch renders its own wrapper or a `render`\n * element.\n */\nexport interface SwatchSharedProps extends HTMLAttributes<HTMLElement> {\n background?: string;\n color?: string;\n /**\n * How multiple `colors` blend: `\"step\"` renders hard bands, `\"smooth\"`\n * renders a continuous OKLab-interpolated ramp. Ignored by\n * `texture=\"atmosphere\"`.\n *\n * Default `\"step\"`.\n */\n blend?: \"smooth\" | \"step\";\n colors?: SwatchColorStop[];\n /**\n * Atmosphere density (0 = diffuse wash, 1 = dense pools). Only applies when\n * `texture=\"atmosphere\"`.\n *\n * Default `0.5`.\n */\n density?: number;\n /**\n * Render a precise, flat color block: no top-to-bottom scrim gradient and\n * no drop shadow. Use for data visualisation where the fill must read as\n * the exact color value.\n *\n * Default `false`.\n */\n flat?: boolean;\n /**\n * Atmosphere gravity (-1 = pools sink, 1 = pools rise). Only applies when\n * `texture=\"atmosphere\"`.\n *\n * Default `0`.\n */\n gravity?: number;\n icon?: SwatchIcon;\n isLight?: boolean;\n /**\n * Object-fit mode applied to media children through CSS variables.\n *\n * Default `\"cover\"`.\n */\n objectFit?: ObjectFit;\n /**\n * Object-position value applied to media children through CSS variables.\n *\n * Default `\"center\"`.\n */\n objectPosition?: string;\n raised?: boolean;\n /**\n * Shows selected state and optional icon overlay.\n *\n * Default `false`.\n */\n selected?: boolean;\n /**\n * Rendered swatch shape.\n *\n * Default `\"circle\"`.\n */\n shape?: SwatchShape;\n /**\n * Whether selected swatches render their ring treatment.\n *\n * Default `true`.\n */\n showRing?: boolean;\n /**\n * Size token used for the swatch dimensions.\n *\n * Default `\"base\"`.\n */\n size?: SwatchSize;\n /**\n * Render supplied colors as a soft, layered radial atmosphere — overlapping\n * color pools — instead of a ratio-encoded weighted palette. Pair with\n * `density` and `gravity` to shape the pools.\n */\n texture?: SwatchTexture;\n /**\n * Renders a visible backdrop behind the fill so a partially transparent\n * Visual Value reads as transparent rather than as white or as absence.\n *\n * Opt in explicitly. CSS alpha cannot be detected reliably across every\n * accepted value — a gradient, a `color-mix()`, a CSS variable or child\n * media can all carry alpha the component cannot see — so this is a\n * declaration by the caller, not an inference.\n *\n * Tune it with `--patternmode-swatch-backdrop-size` and\n * `--patternmode-swatch-backdrop-color`.\n *\n * Default `false`.\n */\n transparencyBackdrop?: boolean;\n /**\n * Marks the swatch as unavailable.\n *\n * Default `false`.\n */\n unavailable?: boolean;\n}\n\n/**\n * Default Swatch props: the swatch renders its own wrapper element\n * (`<figure>`, or `<fieldset>` when `onRemove` is set).\n */\nexport interface SwatchDefaultProps extends SwatchSharedProps {\n render?: undefined;\n /**\n * Optional media rendered inside the swatch frame.\n *\n * Swatch does not optimize image elements itself. Next.js consumers can pass\n * their own `next/image` `Image` component here and use `objectFit` /\n * `objectPosition` to align it with the swatch shape.\n */\n children?: ReactNode;\n /** Renders a remove affordance and calls this after stopping propagation. */\n onRemove?: () => void;\n /** Accessible label for the remove affordance. Defaults to the swatch label. */\n removeLabel?: string;\n}\n\n/**\n * `render` Swatch props: the swatch merges its className, style (size/fill\n * CSS variables), data attributes, and other props onto the element passed via\n * `render`, rendering through it (Base UI render-prop pattern) instead of\n * emitting its own wrapper. Use this when the swatch must *be* an interactive\n * element, such as a `<button>` cell in a color matrix.\n *\n * The `render` element must be childless — put the swatch's content in\n * `children`, e.g. `<Swatch render={<button type=\"button\" />}>A1</Swatch>`.\n * Children on the `render` element itself would override the swatch's own fill\n * layers.\n *\n * `onRemove` is unsupported in this mode — its remove affordance cannot be\n * composed into an arbitrary rendered element. Wrap a default Swatch instead\n * when a remove control is required.\n */\nexport interface SwatchRenderProps extends SwatchSharedProps {\n render: RenderProp;\n children?: ReactNode;\n onRemove?: never;\n removeLabel?: never;\n}\n\nexport type SwatchProps = SwatchDefaultProps | SwatchRenderProps;\n","import { getObjectSizingStyle, isLightColor, joinClassNames } from \"@patternmode/system\";\nimport { isValidElement } from \"react\";\nimport type { CSSProperties, HTMLAttributes, MouseEvent, ReactNode } from \"react\";\n\nimport { useRender } from \"../render\";\nimport type { RenderProp } from \"../render\";\nimport { getSwatchAtmosphereBackground } from \"./swatch-atmosphere\";\nimport { getSwatchColorsBackground } from \"./swatch-colors\";\nimport { getSwatchSizeVariableStyle } from \"./swatch-types\";\nimport type { SwatchProps } from \"./swatch-types\";\n\ntype SwatchRootStyle = CSSProperties & Record<\"--patternmode-swatch-fill\", string | undefined>;\n\ninterface SwatchContentProps {\n children: SwatchProps[\"children\"];\n flat: boolean;\n Icon: SwatchProps[\"icon\"];\n mediaStyle: CSSProperties;\n selected: boolean;\n transparencyBackdrop: boolean;\n unavailable: boolean;\n}\n\ninterface RemovableSwatchProps {\n ariaLabel: string | undefined;\n children: ReactNode;\n className: string | undefined;\n dataProps: ReturnType<typeof getSwatchDataProps>;\n onRemove: () => void;\n removeLabel: string;\n rootStyle: SwatchRootStyle;\n props: HTMLAttributes<HTMLElement>;\n}\n\nconst SwatchContent = ({\n children,\n flat,\n Icon,\n mediaStyle,\n selected,\n transparencyBackdrop,\n unavailable,\n}: SwatchContentProps) => (\n <>\n {/* Behind the fill on purpose: a translucent fill has to composite over it. */}\n {transparencyBackdrop ? (\n <span aria-hidden=\"true\" className=\"patternmode-swatch__backdrop\" />\n ) : null}\n <span aria-hidden=\"true\" className=\"patternmode-swatch__fill\" />\n {children !== undefined && children !== null ? (\n <span className=\"patternmode-swatch__media\" style={mediaStyle}>\n {children}\n </span>\n ) : null}\n {flat ? null : <span aria-hidden=\"true\" className=\"patternmode-swatch__scrim\" />}\n {selected && Icon ? (\n <span className=\"patternmode-swatch__icon\">\n <Icon aria-hidden=\"true\" focusable=\"false\" />\n </span>\n ) : null}\n {unavailable ? <span aria-hidden=\"true\" className=\"patternmode-swatch__slash\" /> : null}\n </>\n);\n\nconst getSwatchFill = ({\n background,\n blend,\n color,\n colors,\n density,\n gravity,\n texture,\n}: Pick<\n SwatchProps,\n \"background\" | \"blend\" | \"color\" | \"colors\" | \"density\" | \"gravity\" | \"texture\"\n>) => {\n const colorsBackground = getSwatchColorsBackground(colors, blend);\n const atmosphereBackground =\n texture === \"atmosphere\"\n ? getSwatchAtmosphereBackground(colors, { density, gravity })\n : undefined;\n\n return {\n colorsBackground,\n fill: background ?? atmosphereBackground ?? colorsBackground ?? color,\n };\n};\n\nconst getSwatchTone = ({\n background,\n color,\n colorsBackground,\n isLight,\n}: {\n background: SwatchProps[\"background\"];\n color: SwatchProps[\"color\"];\n colorsBackground: string | undefined;\n isLight: SwatchProps[\"isLight\"];\n}) => {\n if (isLight !== undefined) {\n return isLight ? \"light\" : \"dark\";\n }\n\n const hasColor = color !== undefined && color !== \"\";\n const hasBackground = background !== undefined && background !== \"\";\n const hasColorsBackground = colorsBackground !== undefined && colorsBackground !== \"\";\n\n if (hasColor && !hasBackground && !hasColorsBackground && isLightColor(color)) {\n return \"light\";\n }\n\n return \"dark\";\n};\n\nconst getSwatchDataProps = ({\n flat,\n lightTone,\n raised,\n selected,\n shape,\n showRing,\n size,\n transparencyBackdrop,\n unavailable,\n}: {\n flat: boolean;\n lightTone: \"dark\" | \"light\";\n raised: boolean;\n selected: boolean;\n shape: SwatchProps[\"shape\"];\n showRing: boolean;\n size: SwatchProps[\"size\"];\n transparencyBackdrop: boolean;\n unavailable: boolean;\n}) => ({\n \"data-flat\": flat ? \"true\" : undefined,\n \"data-raised\": raised ? \"true\" : undefined,\n \"data-selected\": selected ? \"true\" : undefined,\n \"data-shape\": shape,\n \"data-show-ring\": showRing ? \"true\" : \"false\",\n \"data-size\": size,\n \"data-slot\": \"swatch\",\n \"data-tone\": lightTone,\n \"data-transparency-backdrop\": transparencyBackdrop ? \"true\" : undefined,\n \"data-unavailable\": unavailable ? \"true\" : undefined,\n});\n\nconst RemovableSwatch = ({\n ariaLabel,\n children,\n className,\n dataProps,\n onRemove,\n removeLabel,\n rootStyle,\n props,\n}: RemovableSwatchProps) => {\n const handleRemove = (event: MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation();\n onRemove();\n };\n\n return (\n <fieldset\n {...props}\n {...dataProps}\n aria-label={ariaLabel}\n className={joinClassNames(\"patternmode-swatch\", className)}\n style={rootStyle}\n >\n {children}\n <button\n aria-label={removeLabel}\n className=\"patternmode-swatch__remove\"\n onClick={handleRemove}\n type=\"button\"\n >\n <svg aria-hidden=\"true\" fill=\"none\" viewBox=\"0 0 20 20\">\n <path d=\"M5.5 5.5l9 9M14.5 5.5l-9 9\" />\n </svg>\n </button>\n </fieldset>\n );\n};\n\ninterface SwatchElementProps {\n ariaLabel: string | undefined;\n className: string | undefined;\n content: ReactNode;\n dataProps: ReturnType<typeof getSwatchDataProps>;\n props: HTMLAttributes<HTMLElement>;\n render: RenderProp | undefined;\n rootStyle: SwatchRootStyle;\n}\n\n// Renders the swatch through `render` (or a default `<figure>`) via Base UI's\n// `useRender`. Isolated into its own component so the `useRender` hook is\n// always called unconditionally, regardless of which branch `Swatch` takes.\nconst SwatchElement = ({\n ariaLabel,\n className,\n content,\n dataProps,\n props,\n render,\n rootStyle,\n}: SwatchElementProps) =>\n useRender({\n defaultTagName: \"figure\",\n props: {\n ...props,\n ...dataProps,\n \"aria-label\": ariaLabel,\n children: content,\n className: joinClassNames(\"patternmode-swatch\", className),\n style: rootStyle,\n },\n render,\n });\n\nconst warnOnRenderChildren = (render: RenderProp) => {\n if (!isValidElement(render)) {\n return;\n }\n const { props } = render;\n if (\n typeof props === \"object\" &&\n props !== null &&\n \"children\" in props &&\n props.children !== undefined &&\n props.children !== null\n ) {\n console.warn(\n \"Swatch `render` element has its own children, which override the swatch fill layers. \" +\n \"Keep the `render` element childless and pass swatch content as `<Swatch>…</Swatch>` children.\",\n );\n }\n};\n\nexport const Swatch = ({\n \"aria-label\": ariaLabel,\n background,\n blend = \"step\",\n children,\n className,\n color,\n colors,\n density,\n flat = false,\n gravity,\n icon: Icon,\n isLight,\n objectFit,\n objectPosition,\n onRemove,\n raised = false,\n removeLabel,\n render,\n selected = false,\n shape = \"circle\",\n showRing = true,\n size = \"base\",\n style,\n texture,\n transparencyBackdrop = false,\n unavailable = false,\n ...props\n}: SwatchProps) => {\n const { colorsBackground, fill } = getSwatchFill({\n background,\n blend,\n color,\n colors,\n density,\n gravity,\n texture,\n });\n const lightTone = getSwatchTone({\n background,\n color,\n colorsBackground,\n isLight,\n });\n const resolvedRemoveLabel =\n removeLabel ?? (ariaLabel !== undefined && ariaLabel !== \"\" ? `Remove ${ariaLabel}` : \"Remove\");\n\n const rootStyle: SwatchRootStyle = {\n ...getSwatchSizeVariableStyle(size),\n \"--patternmode-swatch-fill\": fill,\n ...style,\n };\n const mediaStyle: CSSProperties = getObjectSizingStyle({\n fit: objectFit,\n position: objectPosition,\n });\n const dataProps = getSwatchDataProps({\n flat,\n lightTone,\n raised,\n selected,\n shape,\n showRing,\n size,\n transparencyBackdrop,\n unavailable,\n });\n\n const swatchContent = (\n <SwatchContent\n flat={flat}\n Icon={Icon}\n mediaStyle={mediaStyle}\n selected={selected}\n transparencyBackdrop={transparencyBackdrop}\n unavailable={unavailable}\n >\n {render ? undefined : children}\n </SwatchContent>\n );\n\n if (render !== undefined) {\n warnOnRenderChildren(render);\n // In `render` mode the swatch fill layers and the consumer's content are\n // composed side by side inside the rendered element (matching the old\n // Slot + Slottable behaviour), so children render unwrapped rather than\n // inside the media frame.\n return (\n <SwatchElement\n ariaLabel={ariaLabel}\n className={className}\n content={\n <>\n {swatchContent}\n {children}\n </>\n }\n dataProps={dataProps}\n props={props}\n render={render}\n rootStyle={rootStyle}\n />\n );\n }\n\n if (onRemove !== undefined) {\n return (\n <RemovableSwatch\n ariaLabel={ariaLabel}\n className={className}\n dataProps={dataProps}\n onRemove={onRemove}\n props={props}\n removeLabel={resolvedRemoveLabel}\n rootStyle={rootStyle}\n >\n {swatchContent}\n </RemovableSwatch>\n );\n }\n\n return (\n <SwatchElement\n ariaLabel={ariaLabel}\n className={className}\n content={swatchContent}\n dataProps={dataProps}\n props={props}\n render={undefined}\n rootStyle={rootStyle}\n />\n );\n};\n"],"mappings":"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"}
package/dist/styles.css CHANGED
@@ -1 +1 @@
1
- @layer components{.patternmode-swatch{--patternmode-swatch-size:2rem;--patternmode-swatch-fill:#e3e1dc;--patternmode-swatch-radius:999px;aspect-ratio:1;border-radius:var(--patternmode-swatch-radius);color:#fff;height:var(--patternmode-swatch-size);vertical-align:middle;min-inline-size:0;width:var(--patternmode-swatch-size);border:0;justify-content:center;align-items:center;margin:0;padding:0;transition:box-shadow .14s,opacity .14s,transform .14s;display:inline-flex;position:relative;box-shadow:0 1px 2px #1118271a}.patternmode-swatch[data-tone=light]{color:#1d1d1b}.patternmode-swatch[data-shape=pill]{--patternmode-swatch-radius:999px;aspect-ratio:auto;width:calc(var(--patternmode-swatch-size) * 3)}.patternmode-swatch[data-shape=square]{--patternmode-swatch-radius:7px}.patternmode-swatch[data-shape=block]{--patternmode-swatch-radius:.25rem;aspect-ratio:auto;width:100%;height:100%;display:block}.patternmode-swatch[data-raised=true]{box-shadow:0 1px 2px #1118271a, 0 0 0 2px var(--card,#fff)}.patternmode-swatch[data-selected=true][data-show-ring=true]{box-shadow:0 1px 2px #1118271a, 0 0 0 2px var(--card,#fff), 0 0 0 4px var(--ring,#315c4b)}.patternmode-swatch[data-unavailable=true]{opacity:.5}.patternmode-swatch[data-flat=true]{box-shadow:none}.patternmode-swatch__fill,.patternmode-swatch__media,.patternmode-swatch__scrim{border-radius:inherit;position:absolute;inset:0;overflow:hidden}.patternmode-swatch__fill{background:var(--patternmode-swatch-fill)}.patternmode-swatch__media>*{object-fit:inherit;object-position:inherit;width:100%;height:100%;display:block}.patternmode-swatch__scrim{pointer-events:none;background:linear-gradient(#0000 0%,#0003 100%)}.patternmode-swatch__icon{justify-content:center;align-items:center;display:inline-flex;position:absolute;inset:0}.patternmode-swatch__icon svg{stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.4px;width:48%;height:48%;display:block}.patternmode-swatch__slash{background:currentColor;border-radius:999px;width:112%;height:2px;position:absolute;transform:rotate(-45deg)}.patternmode-swatch__remove{color:var(--card,#fff);cursor:pointer;opacity:0;background:0 0;border:0;border-radius:999px;justify-content:center;align-items:center;width:2.75rem;height:2.75rem;padding:0;transition:opacity .14s;display:inline-flex;position:absolute;top:-1.1rem;right:-1.1rem}.patternmode-swatch__remove:before{background:var(--foreground,#1d1d1b);border-radius:inherit;content:"";width:1.25rem;height:1.25rem;transition:background .14s;position:absolute}.patternmode-swatch__remove:focus-visible{opacity:1}@media (hover:hover){.patternmode-swatch:hover .patternmode-swatch__remove{opacity:1}.patternmode-swatch__remove:hover:before{background:var(--ring,#315c4b)}}@media (hover:none){.patternmode-swatch__remove{opacity:1}}.patternmode-swatch__remove:focus-visible{outline:2px solid var(--ring,#315c4b);outline-offset:2px}.patternmode-swatch__remove svg{stroke:currentColor;stroke-linecap:round;stroke-width:2px;z-index:1;width:.8rem;height:.8rem}@media (prefers-reduced-motion:reduce){.patternmode-swatch,.patternmode-swatch__remove,.patternmode-swatch__remove:before{transition:none}}.patternmode-distribution-bar,.patternmode-distribution-display{min-inline-size:0;border:0;gap:10px;width:100%;margin:0;padding:0;display:grid}.patternmode-distribution-bar__track{height:var(--patternmode-distribution-height,40px);width:100%;position:relative}.patternmode-distribution-bar .patternmode-distribution-bar__track{touch-action:none}.patternmode-distribution-bar__segments{border:1px solid var(--border,#1118271f);border-radius:var(--patternmode-distribution-radius,999px);width:100%;height:100%;display:flex;overflow:hidden}.patternmode-distribution-bar__segment{background:var(--patternmode-distribution-segment-color);border:0;min-width:0;height:100%;margin:0;padding:0;transition:width .48s cubic-bezier(.2,0,0,1)}.patternmode-distribution-bar[data-dragging=true] .patternmode-distribution-bar__segment{transition:none}button.patternmode-distribution-bar__segment{cursor:pointer}button.patternmode-distribution-bar__segment:focus-visible{outline:2px solid var(--ring,#315c4b);outline-offset:-2px;z-index:2}.patternmode-distribution-bar__segment+.patternmode-distribution-bar__segment{box-shadow:inset 1px 0 #fff9}.patternmode-distribution-bar__segment[data-selected=true]{z-index:2;position:relative}.patternmode-distribution-bar__segment[data-selected=true]:after{border:2px solid var(--card,#fff);content:"";border-radius:999px;width:.62rem;height:.62rem;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);box-shadow:0 1px 4px #11182747,inset 0 0 0 1px #1118271f}@media (prefers-reduced-motion:reduce){.patternmode-distribution-bar__segment{transition:none}}.patternmode-distribution-bar__segment--empty{background-color:var(--muted,#f7f5f0);background-image:repeating-linear-gradient(45deg, transparent, transparent 3px, var(--border,#1118271f) 3px, var(--border,#1118271f) 4px)}.patternmode-distribution-bar__handle{cursor:ew-resize;z-index:1;background:0 0;border:0;border-radius:999px;justify-content:center;align-items:center;width:2.75rem;height:2.75rem;padding:0;display:inline-flex;position:absolute;top:calc(50% - 1.375rem)}.patternmode-distribution-bar__handle:before{background:var(--card,#fff);content:"";border-radius:999px;width:.62rem;height:.62rem;transition:box-shadow .12s,transform .12s;box-shadow:0 1px 4px #1118272e,0 0 0 1px #11182733}.patternmode-distribution-bar__handle:focus-visible{outline:2px solid var(--ring,#315c4b);outline-offset:-4px}@media (hover:hover){.patternmode-distribution-bar__handle:hover:before{transform:scale(1.15);box-shadow:0 5px 14px #1118274d,0 0 0 1px #11182738}}.patternmode-distribution-bar__legend{flex-wrap:wrap;gap:8px 14px;display:flex}.patternmode-distribution-bar__legend>span{color:var(--muted-foreground,#595853);align-items:center;gap:6px;font-size:.68rem;font-weight:500;display:inline-flex}.patternmode-distribution-bar__swatch{background:var(--patternmode-distribution-segment-color);border-radius:999px;width:.55rem;height:.55rem;display:inline-flex}.patternmode-distribution-bar__swatch--empty{background-color:var(--muted,#f7f5f0);background-image:repeating-linear-gradient(45deg, transparent, transparent 2px, var(--border,#1118271f) 2px, var(--border,#1118271f) 3px);box-shadow:inset 0 0 0 1px var(--border,#1118271f)}@media (prefers-reduced-motion:reduce){.patternmode-distribution-bar__handle:before{transition:none}}}
1
+ @layer components{.patternmode-swatch{--patternmode-swatch-size:2rem;--patternmode-swatch-fill:#e3e1dc;--patternmode-swatch-radius:999px;aspect-ratio:1;border-radius:var(--patternmode-swatch-radius);color:#fff;height:var(--patternmode-swatch-size);vertical-align:middle;min-inline-size:0;width:var(--patternmode-swatch-size);border:0;justify-content:center;align-items:center;margin:0;padding:0;transition:box-shadow .14s,opacity .14s,transform .14s;display:inline-flex;position:relative;box-shadow:0 1px 2px #1118271a}.patternmode-swatch[data-tone=light]{color:#1d1d1b}.patternmode-swatch[data-shape=pill]{--patternmode-swatch-radius:999px;aspect-ratio:auto;width:calc(var(--patternmode-swatch-size) * 3)}.patternmode-swatch[data-shape=square]{--patternmode-swatch-radius:7px}.patternmode-swatch[data-shape=block]{--patternmode-swatch-radius:.25rem;aspect-ratio:auto;width:100%;height:100%;display:block}.patternmode-swatch[data-raised=true]{box-shadow:0 1px 2px #1118271a, 0 0 0 2px var(--card,#fff)}.patternmode-swatch[data-selected=true][data-show-ring=true]{box-shadow:0 1px 2px #1118271a, 0 0 0 2px var(--card,#fff), 0 0 0 4px var(--ring,#315c4b)}.patternmode-swatch[data-unavailable=true]{opacity:.5}.patternmode-swatch[data-flat=true]{box-shadow:none}.patternmode-swatch__backdrop,.patternmode-swatch__fill,.patternmode-swatch__media,.patternmode-swatch__scrim{border-radius:inherit;position:absolute;inset:0;overflow:hidden}.patternmode-swatch__backdrop{background-color:var(--patternmode-swatch-backdrop-base,var(--card,#fff));background-image:conic-gradient(var(--patternmode-swatch-backdrop-color,var(--border,#dcdcdc)) 0deg 90deg, transparent 90deg 180deg, var(--patternmode-swatch-backdrop-color,var(--border,#dcdcdc)) 180deg 270deg, transparent 270deg);background-size:var(--patternmode-swatch-backdrop-size,8px) var(--patternmode-swatch-backdrop-size,8px)}.patternmode-swatch__fill{background:var(--patternmode-swatch-fill)}.patternmode-swatch__media>*{object-fit:inherit;object-position:inherit;width:100%;height:100%;display:block}.patternmode-swatch__scrim{pointer-events:none;background:linear-gradient(#0000 0%,#0003 100%)}.patternmode-swatch__icon{justify-content:center;align-items:center;display:inline-flex;position:absolute;inset:0}.patternmode-swatch__icon svg{stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.4px;width:48%;height:48%;display:block}.patternmode-swatch__slash{background:currentColor;border-radius:999px;width:112%;height:2px;position:absolute;transform:rotate(-45deg)}.patternmode-swatch__remove{color:var(--card,#fff);cursor:pointer;opacity:0;background:0 0;border:0;border-radius:999px;justify-content:center;align-items:center;width:2.75rem;height:2.75rem;padding:0;transition:opacity .14s;display:inline-flex;position:absolute;top:-1.1rem;right:-1.1rem}.patternmode-swatch__remove:before{background:var(--foreground,#1d1d1b);border-radius:inherit;content:"";width:1.25rem;height:1.25rem;transition:background .14s;position:absolute}.patternmode-swatch__remove:focus-visible{opacity:1}@media (hover:hover){.patternmode-swatch:hover .patternmode-swatch__remove{opacity:1}.patternmode-swatch__remove:hover:before{background:var(--ring,#315c4b)}}@media (hover:none){.patternmode-swatch__remove{opacity:1}}.patternmode-swatch__remove:focus-visible{outline:2px solid var(--ring,#315c4b);outline-offset:2px}.patternmode-swatch__remove svg{stroke:currentColor;stroke-linecap:round;stroke-width:2px;z-index:1;width:.8rem;height:.8rem}@media (prefers-reduced-motion:reduce){.patternmode-swatch,.patternmode-swatch__remove,.patternmode-swatch__remove:before{transition:none}}.patternmode-distribution-bar,.patternmode-distribution-display{min-inline-size:0;border:0;gap:10px;width:100%;margin:0;padding:0;display:grid}.patternmode-distribution-bar__track{height:var(--patternmode-distribution-height,40px);width:100%;position:relative}.patternmode-distribution-bar .patternmode-distribution-bar__track{touch-action:none}.patternmode-distribution-bar__segments{border:1px solid var(--border,#1118271f);border-radius:var(--patternmode-distribution-radius,999px);width:100%;height:100%;display:flex;overflow:hidden}.patternmode-distribution-bar__segment{background:var(--patternmode-distribution-segment-color);border:0;min-width:0;height:100%;margin:0;padding:0;transition:width .48s cubic-bezier(.2,0,0,1)}.patternmode-distribution-bar[data-dragging=true] .patternmode-distribution-bar__segment{transition:none}button.patternmode-distribution-bar__segment{cursor:pointer}button.patternmode-distribution-bar__segment:focus-visible{outline:2px solid var(--ring,#315c4b);outline-offset:-2px;z-index:2}.patternmode-distribution-bar__segment+.patternmode-distribution-bar__segment{box-shadow:inset 1px 0 #fff9}.patternmode-distribution-bar__segment[data-selected=true]{z-index:2;position:relative}.patternmode-distribution-bar__segment[data-selected=true]:after{border:2px solid var(--card,#fff);content:"";border-radius:999px;width:.62rem;height:.62rem;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);box-shadow:0 1px 4px #11182747,inset 0 0 0 1px #1118271f}@media (prefers-reduced-motion:reduce){.patternmode-distribution-bar__segment{transition:none}}.patternmode-distribution-bar__segment--empty{background-color:var(--muted,#f7f5f0);background-image:repeating-linear-gradient(45deg, transparent, transparent 3px, var(--border,#1118271f) 3px, var(--border,#1118271f) 4px)}.patternmode-distribution-bar__handle{cursor:ew-resize;z-index:1;background:0 0;border:0;border-radius:999px;justify-content:center;align-items:center;width:2.75rem;height:2.75rem;padding:0;display:inline-flex;position:absolute;top:calc(50% - 1.375rem)}.patternmode-distribution-bar__handle:before{background:var(--card,#fff);content:"";border-radius:999px;width:.62rem;height:.62rem;transition:box-shadow .12s,transform .12s;box-shadow:0 1px 4px #1118272e,0 0 0 1px #11182733}.patternmode-distribution-bar__handle:focus-visible{outline:2px solid var(--ring,#315c4b);outline-offset:-4px}@media (hover:hover){.patternmode-distribution-bar__handle:hover:before{transform:scale(1.15);box-shadow:0 5px 14px #1118274d,0 0 0 1px #11182738}}.patternmode-distribution-bar__legend{flex-wrap:wrap;gap:8px 14px;display:flex}.patternmode-distribution-bar__legend>span{color:var(--muted-foreground,#595853);align-items:center;gap:6px;font-size:.68rem;font-weight:500;display:inline-flex}.patternmode-distribution-bar__swatch{background:var(--patternmode-distribution-segment-color);border-radius:999px;width:.55rem;height:.55rem;display:inline-flex}.patternmode-distribution-bar__swatch--empty{background-color:var(--muted,#f7f5f0);background-image:repeating-linear-gradient(45deg, transparent, transparent 2px, var(--border,#1118271f) 2px, var(--border,#1118271f) 3px);box-shadow:inset 0 0 0 1px var(--border,#1118271f)}@media (prefers-reduced-motion:reduce){.patternmode-distribution-bar__handle:before{transition:none}}}
package/dist/swatch.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- export { DistributionBar, type DistributionBarProps, type DistributionBarSegment, type DistributionBarSegmentUpdate, DistributionDisplay, type DistributionDisplayProps, getDistributionBoundaryPercent, getDistributionTotal, moveDistributionBoundary, removeDistributionSegment, updateDistributionSegment, } from "./DistributionBar";
1
+ export { type DistributionBarSegment, type DistributionBarSegmentUpdate, type DistributionSegment, type DistributionSegmentUpdate, getDistributionBoundaryPercent, getDistributionTotal, moveDistributionBoundary, removeDistributionSegment, updateDistributionSegment, } from "./Distribution";
2
+ export { DistributionBar, type DistributionBarProps } from "./DistributionBar";
3
+ export { DistributionDisplay, type DistributionDisplayProps } from "./DistributionDisplay";
2
4
  export { getSwatchAtmosphereBackground, type SwatchAtmosphereOptions, } from "./Swatch/swatch-atmosphere";
3
5
  export { getSwatchColorsBackground } from "./Swatch/swatch-colors";
4
6
  export { Swatch } from "./Swatch/swatch-root";
@@ -1 +1 @@
1
- {"version":3,"file":"swatch.d.ts","sourceRoot":"","sources":["../src/swatch.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,EACjC,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,8BAA8B,EAC9B,oBAAoB,EACpB,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,6BAA6B,EAC7B,KAAK,uBAAuB,GAC7B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EACL,0BAA0B,EAC1B,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,aAAa,GACnB,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"swatch.d.ts","sourceRoot":"","sources":["../src/swatch.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,EACjC,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,8BAA8B,EAC9B,oBAAoB,EACpB,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAC3F,OAAO,EACL,6BAA6B,EAC7B,KAAK,uBAAuB,GAC7B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EACL,0BAA0B,EAC1B,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,aAAa,GACnB,MAAM,uBAAuB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternmode/swatch",
3
- "version": "3.0.0",
3
+ "version": "4.1.0",
4
4
  "private": false,
5
5
  "description": "Color, gradient, image, and palette swatch primitives for Patternmode interfaces.",
6
6
  "keywords": [
@@ -42,13 +42,12 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@base-ui/react": "^1.6.0",
45
- "motion": "^12.40.0",
46
- "@patternmode/system": "0.6.0"
45
+ "@patternmode/system": "0.7.0"
47
46
  },
48
47
  "devDependencies": {
49
48
  "@howells/lint": "^0.5.0",
50
49
  "@howells/typescript-config": "^0.1.6",
51
- "@instruments/colorscope": "^3.17.0",
50
+ "@instruments/colorscope": "^4.0.0",
52
51
  "@tailwindcss/cli": "^4.3.0",
53
52
  "@testing-library/jest-dom": "^6.9.1",
54
53
  "@testing-library/react": "^16.3.2",
@@ -57,15 +56,17 @@
57
56
  "@types/react-dom": "^19.2.3",
58
57
  "concurrently": "^9.2.1",
59
58
  "jsdom": "^29.1.1",
59
+ "motion": "^12.40.0",
60
60
  "react": "^19.2.7",
61
61
  "react-dom": "^19.2.7",
62
62
  "tailwindcss": "^4.3.0",
63
63
  "tsdown": "^0.22.0",
64
64
  "typescript": "^6.0.3",
65
- "vitest": "^4.1.8"
65
+ "vitest": "^4.1.10"
66
66
  },
67
67
  "peerDependencies": {
68
- "@instruments/colorscope": "^3.17.0",
68
+ "@instruments/colorscope": "^3.17.0 || ^4.0.0",
69
+ "motion": "^12.40.0",
69
70
  "react": "^18.0.0 || ^19.0.0",
70
71
  "react-dom": "^18.0.0 || ^19.0.0"
71
72
  },
@@ -1,29 +0,0 @@
1
- import type { WeightedColorSegment } from "@patternmode/system";
2
- /**
3
- * Weighted segment used by DistributionBar and DistributionDisplay. Extends the
4
- * shared {@link WeightedColorSegment} with a required stable `id` for editing.
5
- */
6
- export interface DistributionBarSegment extends WeightedColorSegment {
7
- id: string;
8
- }
9
- /** Segment metadata update; weight changes happen through boundary movement. */
10
- export type DistributionBarSegmentUpdate = Partial<Omit<DistributionBarSegment, "value">> & {
11
- value?: never;
12
- };
13
- /** Sums sanitized segment weights, treating invalid or negative values as 0. */
14
- export declare const getDistributionTotal: (segments: DistributionBarSegment[]) => number;
15
- /** Returns the percentage position of the boundary after `boundaryIndex`. */
16
- export declare const getDistributionBoundaryPercent: (segments: DistributionBarSegment[], boundaryIndex: number) => number;
17
- /**
18
- * Moves the boundary between two adjacent segments while preserving their sum.
19
- *
20
- * `deltaValue` is applied to the left segment and subtracted from the right
21
- * segment. `minValue` prevents either side of the pair from collapsing below a
22
- * caller-defined minimum.
23
- */
24
- export declare const moveDistributionBoundary: (segments: DistributionBarSegment[], boundaryIndex: number, deltaValue: number, minValue: number) => DistributionBarSegment[];
25
- /** Removes a segment and redistributes its weight proportionally to the rest. */
26
- export declare const removeDistributionSegment: (segments: DistributionBarSegment[], segmentId: string) => DistributionBarSegment[];
27
- /** Updates non-weight segment metadata such as label or color. */
28
- export declare const updateDistributionSegment: (segments: DistributionBarSegment[], segmentId: string, update: DistributionBarSegmentUpdate) => DistributionBarSegment[];
29
- //# sourceMappingURL=distribution-bar-math.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"distribution-bar-math.d.ts","sourceRoot":"","sources":["../../src/DistributionBar/distribution-bar-math.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEhE;;;GAGG;AACH,MAAM,WAAW,sBAAuB,SAAQ,oBAAoB;IAClE,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,gFAAgF;AAChF,MAAM,MAAM,4BAA4B,GAAG,OAAO,CAAC,IAAI,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAC,GAAG;IAC1F,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,CAAC;AAOF,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,GAAI,UAAU,sBAAsB,EAAE,KAAG,MACC,CAAC;AAE5E,6EAA6E;AAC7E,eAAO,MAAM,8BAA8B,GACzC,UAAU,sBAAsB,EAAE,EAClC,eAAe,MAAM,KACpB,MAUF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GACnC,UAAU,sBAAsB,EAAE,EAClC,eAAe,MAAM,EACrB,YAAY,MAAM,EAClB,UAAU,MAAM,KACf,sBAAsB,EAyBxB,CAAC;AAEF,iFAAiF;AACjF,eAAO,MAAM,yBAAyB,GACpC,UAAU,sBAAsB,EAAE,EAClC,WAAW,MAAM,KAChB,sBAAsB,EAmCxB,CAAC;AAEF,kEAAkE;AAClE,eAAO,MAAM,yBAAyB,GACpC,UAAU,sBAAsB,EAAE,EAClC,WAAW,MAAM,EACjB,QAAQ,4BAA4B,KACnC,sBAAsB,EACoE,CAAC"}