@xsolla/xui-gradient-picker 0.204.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/web/index.mjs ADDED
@@ -0,0 +1,1049 @@
1
+ // src/GradientPicker.tsx
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+
4
+ // ../../foundation/primitives-web/src/Box.tsx
5
+ import React2 from "react";
6
+ import styled from "styled-components";
7
+
8
+ // ../../foundation/primitives-web/src/filterDOMProps.ts
9
+ import React from "react";
10
+
11
+ // ../../../node_modules/@emotion/memoize/dist/memoize.esm.js
12
+ function memoize(fn) {
13
+ var cache = {};
14
+ return function(arg) {
15
+ if (cache[arg] === void 0) cache[arg] = fn(arg);
16
+ return cache[arg];
17
+ };
18
+ }
19
+ var memoize_esm_default = memoize;
20
+
21
+ // ../../../node_modules/@emotion/is-prop-valid/dist/is-prop-valid.esm.js
22
+ var reactPropsRegex = /^((children|dangerouslySetInnerHTML|key|ref|autoFocus|defaultValue|defaultChecked|innerHTML|suppressContentEditableWarning|suppressHydrationWarning|valueLink|accept|acceptCharset|accessKey|action|allow|allowUserMedia|allowPaymentRequest|allowFullScreen|allowTransparency|alt|async|autoComplete|autoPlay|capture|cellPadding|cellSpacing|challenge|charSet|checked|cite|classID|className|cols|colSpan|content|contentEditable|contextMenu|controls|controlsList|coords|crossOrigin|data|dateTime|decoding|default|defer|dir|disabled|disablePictureInPicture|download|draggable|encType|form|formAction|formEncType|formMethod|formNoValidate|formTarget|frameBorder|headers|height|hidden|high|href|hrefLang|htmlFor|httpEquiv|id|inputMode|integrity|is|keyParams|keyType|kind|label|lang|list|loading|loop|low|marginHeight|marginWidth|max|maxLength|media|mediaGroup|method|min|minLength|multiple|muted|name|nonce|noValidate|open|optimum|pattern|placeholder|playsInline|poster|preload|profile|radioGroup|readOnly|referrerPolicy|rel|required|reversed|role|rows|rowSpan|sandbox|scope|scoped|scrolling|seamless|selected|shape|size|sizes|slot|span|spellCheck|src|srcDoc|srcLang|srcSet|start|step|style|summary|tabIndex|target|title|type|useMap|value|width|wmode|wrap|about|datatype|inlist|prefix|property|resource|typeof|vocab|autoCapitalize|autoCorrect|autoSave|color|inert|itemProp|itemScope|itemType|itemID|itemRef|on|results|security|unselectable|accentHeight|accumulate|additive|alignmentBaseline|allowReorder|alphabetic|amplitude|arabicForm|ascent|attributeName|attributeType|autoReverse|azimuth|baseFrequency|baselineShift|baseProfile|bbox|begin|bias|by|calcMode|capHeight|clip|clipPathUnits|clipPath|clipRule|colorInterpolation|colorInterpolationFilters|colorProfile|colorRendering|contentScriptType|contentStyleType|cursor|cx|cy|d|decelerate|descent|diffuseConstant|direction|display|divisor|dominantBaseline|dur|dx|dy|edgeMode|elevation|enableBackground|end|exponent|externalResourcesRequired|fill|fillOpacity|fillRule|filter|filterRes|filterUnits|floodColor|floodOpacity|focusable|fontFamily|fontSize|fontSizeAdjust|fontStretch|fontStyle|fontVariant|fontWeight|format|from|fr|fx|fy|g1|g2|glyphName|glyphOrientationHorizontal|glyphOrientationVertical|glyphRef|gradientTransform|gradientUnits|hanging|horizAdvX|horizOriginX|ideographic|imageRendering|in|in2|intercept|k|k1|k2|k3|k4|kernelMatrix|kernelUnitLength|kerning|keyPoints|keySplines|keyTimes|lengthAdjust|letterSpacing|lightingColor|limitingConeAngle|local|markerEnd|markerMid|markerStart|markerHeight|markerUnits|markerWidth|mask|maskContentUnits|maskUnits|mathematical|mode|numOctaves|offset|opacity|operator|order|orient|orientation|origin|overflow|overlinePosition|overlineThickness|panose1|paintOrder|pathLength|patternContentUnits|patternTransform|patternUnits|pointerEvents|points|pointsAtX|pointsAtY|pointsAtZ|preserveAlpha|preserveAspectRatio|primitiveUnits|r|radius|refX|refY|renderingIntent|repeatCount|repeatDur|requiredExtensions|requiredFeatures|restart|result|rotate|rx|ry|scale|seed|shapeRendering|slope|spacing|specularConstant|specularExponent|speed|spreadMethod|startOffset|stdDeviation|stemh|stemv|stitchTiles|stopColor|stopOpacity|strikethroughPosition|strikethroughThickness|string|stroke|strokeDasharray|strokeDashoffset|strokeLinecap|strokeLinejoin|strokeMiterlimit|strokeOpacity|strokeWidth|surfaceScale|systemLanguage|tableValues|targetX|targetY|textAnchor|textDecoration|textRendering|textLength|to|transform|u1|u2|underlinePosition|underlineThickness|unicode|unicodeBidi|unicodeRange|unitsPerEm|vAlphabetic|vHanging|vIdeographic|vMathematical|values|vectorEffect|version|vertAdvY|vertOriginX|vertOriginY|viewBox|viewTarget|visibility|widths|wordSpacing|writingMode|x|xHeight|x1|x2|xChannelSelector|xlinkActuate|xlinkArcrole|xlinkHref|xlinkRole|xlinkShow|xlinkTitle|xlinkType|xmlBase|xmlns|xmlnsXlink|xmlLang|xmlSpace|y|y1|y2|yChannelSelector|z|zoomAndPan|for|class|autofocus)|(([Dd][Aa][Tt][Aa]|[Aa][Rr][Ii][Aa]|x)-.*))$/;
23
+ var index = memoize_esm_default(
24
+ function(prop) {
25
+ return reactPropsRegex.test(prop) || prop.charCodeAt(0) === 111 && prop.charCodeAt(1) === 110 && prop.charCodeAt(2) < 91;
26
+ }
27
+ /* Z+1 */
28
+ );
29
+ var is_prop_valid_esm_default = index;
30
+
31
+ // ../../foundation/primitives-web/src/filterDOMProps.ts
32
+ var ADDITIONAL_BLOCKED_PROPS = /* @__PURE__ */ new Set([
33
+ // RN-only event handlers (pass isPropValid's on* pattern)
34
+ "onPress",
35
+ "onChangeText",
36
+ "onLayout",
37
+ "onMoveShouldSetResponder",
38
+ "onResponderGrant",
39
+ "onResponderMove",
40
+ "onResponderRelease",
41
+ "onResponderTerminate",
42
+ // SVG attributes that pass isPropValid
43
+ "strokeWidth",
44
+ // CSS properties that pass isPropValid but are used as component props
45
+ "overflow",
46
+ "opacity",
47
+ "cursor",
48
+ "fontSize",
49
+ "fontWeight",
50
+ "fontFamily",
51
+ "textDecoration",
52
+ // Cross-platform layout prop that Text consumes as CSS, never as an
53
+ // attribute. Pinned explicitly because it is now spread into the styled
54
+ // component rather than destructured away before it can reach the DOM.
55
+ "numberOfLines"
56
+ ]);
57
+ function shouldForwardProp(key) {
58
+ if (ADDITIONAL_BLOCKED_PROPS.has(key)) return false;
59
+ return is_prop_valid_esm_default(key);
60
+ }
61
+ function createFilteredElement(defaultTag) {
62
+ const Component = React.forwardRef(
63
+ ({ children, elementType, ...props }, ref) => {
64
+ const Tag = elementType || defaultTag;
65
+ const htmlProps = {};
66
+ for (const key of Object.keys(props)) {
67
+ if (shouldForwardProp(key)) {
68
+ htmlProps[key] = props[key];
69
+ }
70
+ }
71
+ return React.createElement(
72
+ Tag,
73
+ { ref, ...htmlProps },
74
+ children
75
+ );
76
+ }
77
+ );
78
+ Component.displayName = `Filtered(${defaultTag})`;
79
+ return Component;
80
+ }
81
+
82
+ // ../../foundation/primitives-web/src/Box.tsx
83
+ import { jsx } from "react/jsx-runtime";
84
+ var FilteredDiv = createFilteredElement("div");
85
+ var StyledBox = styled(FilteredDiv)`
86
+ display: flex;
87
+ box-sizing: border-box;
88
+ background-color: ${(props) => props.backgroundColor || "transparent"};
89
+ border-color: ${(props) => props.borderColor || "transparent"};
90
+ border-width: ${(props) => typeof props.borderWidth === "number" ? `${props.borderWidth}px` : props.borderWidth || 0};
91
+
92
+ ${(props) => props.borderBottomWidth !== void 0 && `
93
+ border-bottom-width: ${typeof props.borderBottomWidth === "number" ? `${props.borderBottomWidth}px` : props.borderBottomWidth};
94
+ border-bottom-color: ${props.borderBottomColor || props.borderColor || "transparent"};
95
+ border-bottom-style: solid;
96
+ `}
97
+ ${(props) => props.borderTopWidth !== void 0 && `
98
+ border-top-width: ${typeof props.borderTopWidth === "number" ? `${props.borderTopWidth}px` : props.borderTopWidth};
99
+ border-top-color: ${props.borderTopColor || props.borderColor || "transparent"};
100
+ border-top-style: solid;
101
+ `}
102
+ ${(props) => props.borderLeftWidth !== void 0 && `
103
+ border-left-width: ${typeof props.borderLeftWidth === "number" ? `${props.borderLeftWidth}px` : props.borderLeftWidth};
104
+ border-left-color: ${props.borderLeftColor || props.borderColor || "transparent"};
105
+ border-left-style: solid;
106
+ `}
107
+ ${(props) => props.borderRightWidth !== void 0 && `
108
+ border-right-width: ${typeof props.borderRightWidth === "number" ? `${props.borderRightWidth}px` : props.borderRightWidth};
109
+ border-right-color: ${props.borderRightColor || props.borderColor || "transparent"};
110
+ border-right-style: solid;
111
+ `}
112
+
113
+ border-style: ${(props) => props.borderStyle || (props.borderWidth || props.borderBottomWidth || props.borderTopWidth || props.borderLeftWidth || props.borderRightWidth ? "solid" : "none")};
114
+ border-radius: ${(props) => typeof props.borderRadius === "number" ? `${props.borderRadius}px` : props.borderRadius || 0};
115
+ height: ${(props) => typeof props.height === "number" ? `${props.height}px` : props.height || "auto"};
116
+ width: ${(props) => typeof props.width === "number" ? `${props.width}px` : props.width || "auto"};
117
+ min-width: ${(props) => typeof props.minWidth === "number" ? `${props.minWidth}px` : props.minWidth || "auto"};
118
+ min-height: ${(props) => typeof props.minHeight === "number" ? `${props.minHeight}px` : props.minHeight || "auto"};
119
+ max-width: ${(props) => typeof props.maxWidth === "number" ? `${props.maxWidth}px` : props.maxWidth || "none"};
120
+ max-height: ${(props) => typeof props.maxHeight === "number" ? `${props.maxHeight}px` : props.maxHeight || "none"};
121
+
122
+ padding: ${(props) => typeof props.padding === "number" ? `${props.padding}px` : props.padding || 0};
123
+ ${(props) => props.paddingHorizontal && `
124
+ padding-left: ${typeof props.paddingHorizontal === "number" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};
125
+ padding-right: ${typeof props.paddingHorizontal === "number" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};
126
+ `}
127
+ ${(props) => props.paddingVertical && `
128
+ padding-top: ${typeof props.paddingVertical === "number" ? `${props.paddingVertical}px` : props.paddingVertical};
129
+ padding-bottom: ${typeof props.paddingVertical === "number" ? `${props.paddingVertical}px` : props.paddingVertical};
130
+ `}
131
+ ${(props) => props.paddingTop !== void 0 && `padding-top: ${typeof props.paddingTop === "number" ? `${props.paddingTop}px` : props.paddingTop};`}
132
+ ${(props) => props.paddingBottom !== void 0 && `padding-bottom: ${typeof props.paddingBottom === "number" ? `${props.paddingBottom}px` : props.paddingBottom};`}
133
+ ${(props) => props.paddingLeft !== void 0 && `padding-left: ${typeof props.paddingLeft === "number" ? `${props.paddingLeft}px` : props.paddingLeft};`}
134
+ ${(props) => props.paddingRight !== void 0 && `padding-right: ${typeof props.paddingRight === "number" ? `${props.paddingRight}px` : props.paddingRight};`}
135
+
136
+ margin: ${(props) => typeof props.margin === "number" ? `${props.margin}px` : props.margin || 0};
137
+ ${(props) => props.marginTop !== void 0 && `margin-top: ${typeof props.marginTop === "number" ? `${props.marginTop}px` : props.marginTop};`}
138
+ ${(props) => props.marginBottom !== void 0 && `margin-bottom: ${typeof props.marginBottom === "number" ? `${props.marginBottom}px` : props.marginBottom};`}
139
+ ${(props) => props.marginLeft !== void 0 && `margin-left: ${typeof props.marginLeft === "number" ? `${props.marginLeft}px` : props.marginLeft};`}
140
+ ${(props) => props.marginRight !== void 0 && `margin-right: ${typeof props.marginRight === "number" ? `${props.marginRight}px` : props.marginRight};`}
141
+
142
+ flex-direction: ${(props) => props.flexDirection || "column"};
143
+ flex-wrap: ${(props) => props.flexWrap || "nowrap"};
144
+ align-items: ${(props) => props.alignItems || "stretch"};
145
+ justify-content: ${(props) => props.justifyContent || "flex-start"};
146
+ cursor: ${(props) => props.cursor ? props.cursor : props.onClick || props.onPress ? "pointer" : "inherit"};
147
+ position: ${(props) => props.position || "static"};
148
+ top: ${(props) => typeof props.top === "number" ? `${props.top}px` : props.top};
149
+ bottom: ${(props) => typeof props.bottom === "number" ? `${props.bottom}px` : props.bottom};
150
+ left: ${(props) => typeof props.left === "number" ? `${props.left}px` : props.left};
151
+ right: ${(props) => typeof props.right === "number" ? `${props.right}px` : props.right};
152
+ flex: ${(props) => props.flex};
153
+ flex-shrink: ${(props) => props.flexShrink ?? 1};
154
+ gap: ${(props) => typeof props.gap === "number" ? `${props.gap}px` : props.gap || 0};
155
+ align-self: ${(props) => props.alignSelf || "auto"};
156
+ /* Only emit overflow-x/y when explicitly set. Defaulting them to
157
+ visible after overflow would override the shorthand and break
158
+ clipping (e.g. OtherCard bottom-right promo image). */
159
+ overflow: ${(props) => props.overflow || "visible"};
160
+ ${(props) => props.overflowX != null ? `overflow-x: ${props.overflowX};` : ""}
161
+ ${(props) => props.overflowY != null ? `overflow-y: ${props.overflowY};` : ""}
162
+ z-index: ${(props) => props.zIndex};
163
+ opacity: ${(props) => props.opacity != null ? props.opacity : props.disabled ? 0.5 : 1};
164
+ pointer-events: ${(props) => props.disabled ? "none" : "auto"};
165
+
166
+ &:hover {
167
+ ${(props) => props.hoverStyle?.backgroundColor && `background-color: ${props.hoverStyle.backgroundColor};`}
168
+ ${(props) => props.hoverStyle?.borderColor && `border-color: ${props.hoverStyle.borderColor};`}
169
+ }
170
+
171
+ &:active {
172
+ ${(props) => props.pressStyle?.backgroundColor && `background-color: ${props.pressStyle.backgroundColor};`}
173
+ }
174
+ `;
175
+ var Box = React2.forwardRef(
176
+ ({
177
+ children,
178
+ onPress,
179
+ onKeyDown,
180
+ onKeyUp,
181
+ role,
182
+ "aria-label": ariaLabel,
183
+ "aria-labelledby": ariaLabelledBy,
184
+ "aria-current": ariaCurrent,
185
+ "aria-disabled": ariaDisabled,
186
+ "aria-live": ariaLive,
187
+ "aria-busy": ariaBusy,
188
+ "aria-describedby": ariaDescribedBy,
189
+ "aria-expanded": ariaExpanded,
190
+ "aria-haspopup": ariaHasPopup,
191
+ "aria-pressed": ariaPressed,
192
+ "aria-controls": ariaControls,
193
+ tabIndex,
194
+ as,
195
+ src,
196
+ alt,
197
+ onError,
198
+ onLoad,
199
+ type,
200
+ disabled,
201
+ id,
202
+ testID,
203
+ "data-testid": dataTestId,
204
+ ...props
205
+ }, ref) => {
206
+ if (as === "img" && src) {
207
+ return /* @__PURE__ */ jsx(
208
+ "img",
209
+ {
210
+ src,
211
+ alt: alt || "",
212
+ onError,
213
+ onLoad,
214
+ "data-testid": dataTestId || testID,
215
+ style: {
216
+ display: "block",
217
+ objectFit: "cover",
218
+ width: typeof props.width === "number" ? `${props.width}px` : props.width,
219
+ height: typeof props.height === "number" ? `${props.height}px` : props.height,
220
+ borderRadius: typeof props.borderRadius === "number" ? `${props.borderRadius}px` : props.borderRadius,
221
+ position: props.position,
222
+ top: typeof props.top === "number" ? `${props.top}px` : props.top,
223
+ left: typeof props.left === "number" ? `${props.left}px` : props.left,
224
+ right: typeof props.right === "number" ? `${props.right}px` : props.right,
225
+ bottom: typeof props.bottom === "number" ? `${props.bottom}px` : props.bottom,
226
+ ...props.style
227
+ }
228
+ }
229
+ );
230
+ }
231
+ return /* @__PURE__ */ jsx(
232
+ StyledBox,
233
+ {
234
+ ref,
235
+ elementType: as,
236
+ id,
237
+ type: as === "button" ? type || "button" : void 0,
238
+ disabled: as === "button" ? disabled : void 0,
239
+ onClick: onPress,
240
+ onKeyDown,
241
+ onKeyUp,
242
+ role,
243
+ "aria-label": ariaLabel,
244
+ "aria-labelledby": ariaLabelledBy,
245
+ "aria-current": ariaCurrent,
246
+ "aria-disabled": ariaDisabled,
247
+ "aria-busy": ariaBusy,
248
+ "aria-describedby": ariaDescribedBy,
249
+ "aria-expanded": ariaExpanded,
250
+ "aria-haspopup": ariaHasPopup,
251
+ "aria-pressed": ariaPressed,
252
+ "aria-controls": ariaControls,
253
+ "aria-live": ariaLive,
254
+ tabIndex: tabIndex !== void 0 ? tabIndex : void 0,
255
+ "data-testid": dataTestId || testID,
256
+ ...props,
257
+ children
258
+ }
259
+ );
260
+ }
261
+ );
262
+ Box.displayName = "Box";
263
+
264
+ // ../../foundation/primitives-web/src/Text.tsx
265
+ import styled2 from "styled-components";
266
+
267
+ // ../../foundation/primitives-web/src/textTruncation.ts
268
+ var truncationStyles = (numberOfLines) => {
269
+ if (!numberOfLines || numberOfLines < 1) return "";
270
+ if (numberOfLines === 1) {
271
+ return `
272
+ display: block;
273
+ max-width: 100%;
274
+ min-width: 0;
275
+ overflow: hidden;
276
+ text-overflow: ellipsis;
277
+ white-space: nowrap;
278
+ `;
279
+ }
280
+ return `
281
+ display: -webkit-box;
282
+ -webkit-box-orient: vertical;
283
+ -webkit-line-clamp: ${numberOfLines};
284
+ line-clamp: ${numberOfLines};
285
+ max-width: 100%;
286
+ min-width: 0;
287
+ overflow: hidden;
288
+ `;
289
+ };
290
+
291
+ // ../../foundation/primitives-web/src/Text.tsx
292
+ import { jsx as jsx2 } from "react/jsx-runtime";
293
+ var FilteredSpan = createFilteredElement("span");
294
+ var StyledText = styled2(FilteredSpan)`
295
+ color: ${(props) => props.color || "inherit"};
296
+ font-size: ${(props) => typeof props.fontSize === "number" ? `${props.fontSize}px` : props.fontSize || "inherit"};
297
+ font-weight: ${(props) => props.fontWeight || "normal"};
298
+ font-family: ${(props) => props.fontFamily || '"Aktiv Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'};
299
+ line-height: ${(props) => typeof props.lineHeight === "number" ? `${props.lineHeight}px` : props.lineHeight || "inherit"};
300
+ white-space: ${(props) => props.whiteSpace || "normal"};
301
+ text-align: ${(props) => props.textAlign || "inherit"};
302
+ text-decoration: ${(props) => props.textDecoration || "none"};
303
+
304
+ /* Keep last: single-line truncation has to win over white-space above. */
305
+ ${(props) => truncationStyles(props.numberOfLines)}
306
+ `;
307
+ var Text = ({
308
+ style,
309
+ className,
310
+ id,
311
+ role,
312
+ testID,
313
+ "data-testid": dataTestId,
314
+ ...props
315
+ }) => {
316
+ return /* @__PURE__ */ jsx2(
317
+ StyledText,
318
+ {
319
+ ...props,
320
+ style,
321
+ className,
322
+ id,
323
+ role,
324
+ "data-testid": dataTestId || testID
325
+ }
326
+ );
327
+ };
328
+
329
+ // ../../foundation/primitives-web/src/index.tsx
330
+ var isWeb = true;
331
+
332
+ // src/GradientPicker.tsx
333
+ import { useResolvedTheme as useResolvedTheme2 } from "@xsolla/xui-core";
334
+ import { Input } from "@xsolla/xui-input";
335
+ import { InputColor } from "@xsolla/xui-input-color";
336
+ import { Select } from "@xsolla/xui-select";
337
+ import { IconButton } from "@xsolla/xui-button";
338
+ import { ColorPicker } from "@xsolla/xui-color-picker";
339
+
340
+ // src/GradientStopMarker.tsx
341
+ import { useResolvedTheme } from "@xsolla/xui-core";
342
+
343
+ // src/utils.ts
344
+ var MIN_POSITION = 0;
345
+ var MAX_POSITION = 100;
346
+ var clampPosition = (position) => {
347
+ if (!Number.isFinite(position)) return MIN_POSITION;
348
+ return Math.min(MAX_POSITION, Math.max(MIN_POSITION, Math.round(position)));
349
+ };
350
+ var clampOpacity = (opacity) => {
351
+ if (!Number.isFinite(opacity)) return 100;
352
+ return Math.min(100, Math.max(0, Math.round(opacity)));
353
+ };
354
+ var normalizeHex = (hex) => {
355
+ const raw = String(hex ?? "").trim().replace(/^#/, "");
356
+ if (/^[0-9a-fA-F]{3}$/.test(raw)) {
357
+ return `#${raw.split("").map((char) => char + char).join("")}`.toUpperCase();
358
+ }
359
+ if (/^[0-9a-fA-F]{6}$/.test(raw)) return `#${raw}`.toUpperCase();
360
+ if (/^[0-9a-fA-F]{8}$/.test(raw)) return `#${raw.slice(0, 6)}`.toUpperCase();
361
+ return "#000000";
362
+ };
363
+ var hexToRgb = (hex) => {
364
+ const normalized = normalizeHex(hex).slice(1);
365
+ return {
366
+ r: parseInt(normalized.slice(0, 2), 16),
367
+ g: parseInt(normalized.slice(2, 4), 16),
368
+ b: parseInt(normalized.slice(4, 6), 16)
369
+ };
370
+ };
371
+ var rgbToHex = (r, g, b) => {
372
+ const toChannel = (channel) => Math.min(255, Math.max(0, Math.round(channel))).toString(16).padStart(2, "0");
373
+ return `#${toChannel(r)}${toChannel(g)}${toChannel(b)}`.toUpperCase();
374
+ };
375
+ var sortStops = (stops) => stops.map((stop, index2) => ({ stop, index: index2 })).sort(
376
+ (a, b) => a.stop.position === b.stop.position ? a.index - b.index : a.stop.position - b.stop.position
377
+ ).map(({ stop }) => stop);
378
+ var stopToCssColor = (stop) => {
379
+ const opacity = clampOpacity(stop.opacity);
380
+ if (opacity >= 100) return normalizeHex(stop.color);
381
+ const { r, g, b } = hexToRgb(stop.color);
382
+ return `rgba(${r}, ${g}, ${b}, ${Number((opacity / 100).toFixed(2))})`;
383
+ };
384
+ var stopsToCssStopList = (stops) => {
385
+ if (stops.length === 0) return "transparent 0%, transparent 100%";
386
+ if (stops.length === 1) {
387
+ const color = stopToCssColor(stops[0]);
388
+ return `${color} 0%, ${color} 100%`;
389
+ }
390
+ return sortStops(stops).map((stop) => `${stopToCssColor(stop)} ${clampPosition(stop.position)}%`).join(", ");
391
+ };
392
+ var toCssGradient = (value) => {
393
+ const stopList = stopsToCssStopList(value.stops);
394
+ const angle = Number.isFinite(value.angle) ? Math.round(value.angle) : 0;
395
+ switch (value.type) {
396
+ case "radial":
397
+ return `radial-gradient(circle, ${stopList})`;
398
+ case "angular":
399
+ return `conic-gradient(from ${angle}deg, ${stopList})`;
400
+ case "linear":
401
+ default:
402
+ return `linear-gradient(${angle}deg, ${stopList})`;
403
+ }
404
+ };
405
+ var toPreviewCssGradient = (value) => `linear-gradient(90deg, ${stopsToCssStopList(value.stops)})`;
406
+ var supportsAngle = (value) => value === "linear" || value === "angular";
407
+ var interpolateColorAt = (stops, position) => {
408
+ if (stops.length === 0) return { color: "#000000", opacity: 100 };
409
+ const sorted = sortStops(stops);
410
+ const target = clampPosition(position);
411
+ const first = sorted[0];
412
+ if (target <= first.position) {
413
+ return {
414
+ color: normalizeHex(first.color),
415
+ opacity: clampOpacity(first.opacity)
416
+ };
417
+ }
418
+ const last = sorted[sorted.length - 1];
419
+ if (target >= last.position) {
420
+ return {
421
+ color: normalizeHex(last.color),
422
+ opacity: clampOpacity(last.opacity)
423
+ };
424
+ }
425
+ for (let index2 = 0; index2 < sorted.length - 1; index2 += 1) {
426
+ const from = sorted[index2];
427
+ const to = sorted[index2 + 1];
428
+ if (target < from.position || target > to.position) continue;
429
+ const span = to.position - from.position;
430
+ const ratio = span === 0 ? 0 : (target - from.position) / span;
431
+ const fromRgb = hexToRgb(from.color);
432
+ const toRgb = hexToRgb(to.color);
433
+ return {
434
+ color: rgbToHex(
435
+ fromRgb.r + (toRgb.r - fromRgb.r) * ratio,
436
+ fromRgb.g + (toRgb.g - fromRgb.g) * ratio,
437
+ fromRgb.b + (toRgb.b - fromRgb.b) * ratio
438
+ ),
439
+ opacity: clampOpacity(
440
+ clampOpacity(from.opacity) + (clampOpacity(to.opacity) - clampOpacity(from.opacity)) * ratio
441
+ )
442
+ };
443
+ }
444
+ return {
445
+ color: normalizeHex(last.color),
446
+ opacity: clampOpacity(last.opacity)
447
+ };
448
+ };
449
+ var createStopId = (stops) => {
450
+ const highest = stops.reduce((max, stop) => {
451
+ const match = /^stop-(\d+)$/.exec(stop.id);
452
+ return match ? Math.max(max, Number(match[1])) : max;
453
+ }, 0);
454
+ return `stop-${highest + 1}`;
455
+ };
456
+ var addStopAt = (stops, position, maxStops) => {
457
+ if (stops.length >= maxStops) return stops;
458
+ const target = clampPosition(position);
459
+ const { color, opacity } = interpolateColorAt(stops, target);
460
+ return sortStops([
461
+ ...stops,
462
+ { id: createStopId(stops), color, position: target, opacity }
463
+ ]);
464
+ };
465
+ var removeStop = (stops, id, minStops) => {
466
+ if (stops.length <= minStops) return stops;
467
+ const next = stops.filter((stop) => stop.id !== id);
468
+ return next.length === stops.length ? stops : next;
469
+ };
470
+ var updateStop = (stops, id, patch) => stops.map((stop) => {
471
+ if (stop.id !== id) return stop;
472
+ return {
473
+ ...stop,
474
+ ...patch,
475
+ color: patch.color === void 0 ? stop.color : normalizeHex(patch.color),
476
+ position: patch.position === void 0 ? stop.position : clampPosition(patch.position),
477
+ opacity: patch.opacity === void 0 ? stop.opacity : clampOpacity(patch.opacity)
478
+ };
479
+ });
480
+ var DEFAULT_GRADIENT = {
481
+ type: "linear",
482
+ angle: 0,
483
+ stops: [
484
+ { id: "stop-1", color: "#D9D9D9", position: 0, opacity: 100 },
485
+ { id: "stop-2", color: "#22A8C3", position: 100, opacity: 100 }
486
+ ]
487
+ };
488
+
489
+ // src/GradientStopMarker.tsx
490
+ import { jsx as jsx3 } from "react/jsx-runtime";
491
+ var GradientStopMarker = ({
492
+ stop,
493
+ index: index2,
494
+ active = false,
495
+ disabled = false,
496
+ onDragStart,
497
+ onKeyDown,
498
+ themeMode,
499
+ themeProductContext
500
+ }) => {
501
+ const { theme } = useResolvedTheme({ themeMode, themeProductContext });
502
+ const size = active ? 18 : 16;
503
+ return /* @__PURE__ */ jsx3(
504
+ Box,
505
+ {
506
+ role: "slider",
507
+ tabIndex: disabled ? -1 : 0,
508
+ "aria-label": `Stop ${index2} position`,
509
+ "aria-valuenow": stop.position,
510
+ "aria-valuemin": MIN_POSITION,
511
+ "aria-valuemax": MAX_POSITION,
512
+ "aria-valuetext": `${stop.position}%`,
513
+ "aria-orientation": "horizontal",
514
+ "aria-disabled": disabled || void 0,
515
+ testID: `gradient-stop-${stop.id}`,
516
+ "data-testid": `gradient-stop-${stop.id}`,
517
+ position: "absolute",
518
+ left: `${stop.position}%`,
519
+ width: size,
520
+ height: size,
521
+ borderRadius: 999,
522
+ zIndex: active ? 3 : 2,
523
+ cursor: disabled ? "not-allowed" : "grab",
524
+ onMouseDown: isWeb && !disabled ? onDragStart : void 0,
525
+ onKeyDown: disabled ? void 0 : onKeyDown,
526
+ style: {
527
+ top: "50%",
528
+ transform: "translate(-50%, -50%)",
529
+ boxSizing: "border-box",
530
+ borderStyle: "solid",
531
+ borderWidth: 3,
532
+ borderColor: theme.colors.background.primary,
533
+ backgroundColor: stopToCssColor(stop),
534
+ boxShadow: active ? "0px 0px 0px 2px rgba(7, 7, 8, 0.25)" : "0px 1px 2px rgba(7, 7, 8, 0.2), 0px 1px 3px 1px rgba(7, 7, 8, 0.1)",
535
+ transition: "width 0.1s ease, height 0.1s ease, box-shadow 0.1s ease",
536
+ outline: "none"
537
+ }
538
+ }
539
+ );
540
+ };
541
+ GradientStopMarker.displayName = "GradientStopMarker";
542
+
543
+ // src/icons.tsx
544
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
545
+ var PlusGlyph = ({
546
+ size = 18,
547
+ color = "currentColor"
548
+ }) => /* @__PURE__ */ jsx4(
549
+ "svg",
550
+ {
551
+ viewBox: "0 0 24 24",
552
+ width: size,
553
+ height: size,
554
+ fill: "none",
555
+ stroke: color,
556
+ strokeWidth: "2",
557
+ strokeLinecap: "round",
558
+ "aria-hidden": "true",
559
+ focusable: "false",
560
+ children: /* @__PURE__ */ jsx4("path", { d: "M12 5v14M5 12h14" })
561
+ }
562
+ );
563
+ var MinusGlyph = ({
564
+ size = 18,
565
+ color = "currentColor"
566
+ }) => /* @__PURE__ */ jsx4(
567
+ "svg",
568
+ {
569
+ viewBox: "0 0 24 24",
570
+ width: size,
571
+ height: size,
572
+ fill: "none",
573
+ stroke: color,
574
+ strokeWidth: "2",
575
+ strokeLinecap: "round",
576
+ "aria-hidden": "true",
577
+ focusable: "false",
578
+ children: /* @__PURE__ */ jsx4("path", { d: "M5 12h14" })
579
+ }
580
+ );
581
+ var AngleGlyph = ({
582
+ size = 16,
583
+ color = "currentColor"
584
+ }) => /* @__PURE__ */ jsx4(
585
+ "svg",
586
+ {
587
+ viewBox: "0 0 24 24",
588
+ width: size,
589
+ height: size,
590
+ fill: "none",
591
+ stroke: color,
592
+ strokeWidth: "2",
593
+ strokeLinecap: "round",
594
+ strokeLinejoin: "round",
595
+ "aria-hidden": "true",
596
+ focusable: "false",
597
+ children: /* @__PURE__ */ jsx4("path", { d: "M4 20h16L4 4z" })
598
+ }
599
+ );
600
+ var PercentGlyph = ({
601
+ size = 16,
602
+ color = "currentColor"
603
+ }) => /* @__PURE__ */ jsxs(
604
+ "svg",
605
+ {
606
+ viewBox: "0 0 24 24",
607
+ width: size,
608
+ height: size,
609
+ fill: "none",
610
+ stroke: color,
611
+ strokeWidth: "2",
612
+ strokeLinecap: "round",
613
+ "aria-hidden": "true",
614
+ focusable: "false",
615
+ children: [
616
+ /* @__PURE__ */ jsx4("circle", { cx: "6.5", cy: "6.5", r: "2.5" }),
617
+ /* @__PURE__ */ jsx4("circle", { cx: "17.5", cy: "17.5", r: "2.5" }),
618
+ /* @__PURE__ */ jsx4("path", { d: "M19 5 5 19" })
619
+ ]
620
+ }
621
+ );
622
+
623
+ // src/GradientPicker.tsx
624
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
625
+ var DEFAULT_TYPE_LABELS = {
626
+ linear: "Linear gradient",
627
+ radial: "Radial gradient",
628
+ angular: "Angular gradient"
629
+ };
630
+ var ALL_TYPES = ["linear", "radial", "angular"];
631
+ var DRAG_OFF_THRESHOLD = 32;
632
+ var TRACK_HEIGHT = 16;
633
+ var MARKER_INSET = 9;
634
+ var toHexa = (color, opacity) => {
635
+ const alpha = Math.round(clampOpacity(opacity) / 100 * 255).toString(16).padStart(2, "0").toUpperCase();
636
+ return `${normalizeHex(color)}${alpha}`;
637
+ };
638
+ var fromHexa = (value) => {
639
+ const raw = String(value ?? "").trim().replace(/^#/, "");
640
+ if (/^[0-9a-fA-F]{8}$/.test(raw)) {
641
+ return {
642
+ color: normalizeHex(raw.slice(0, 6)),
643
+ opacity: clampOpacity(parseInt(raw.slice(6, 8), 16) / 255 * 100)
644
+ };
645
+ }
646
+ return { color: normalizeHex(raw), opacity: 100 };
647
+ };
648
+ var GradientPicker = ({
649
+ value: propValue,
650
+ defaultValue,
651
+ onChange,
652
+ gradientTypes = ALL_TYPES,
653
+ typeLabels,
654
+ minStops = 1,
655
+ maxStops = 5,
656
+ disabled = false,
657
+ typeAriaLabel = "Gradient type",
658
+ angleAriaLabel = "Gradient angle",
659
+ stopsLabel = "Stops",
660
+ testID,
661
+ themeMode,
662
+ themeProductContext
663
+ }) => {
664
+ const { theme } = useResolvedTheme2({ themeMode, themeProductContext });
665
+ const contentColors = theme.colors.content;
666
+ const themeProps = { themeMode, themeProductContext };
667
+ const isControlled = propValue !== void 0;
668
+ const [innerValue, setInnerValue] = useState(
669
+ () => propValue ?? defaultValue ?? DEFAULT_GRADIENT
670
+ );
671
+ const value = isControlled ? propValue : innerValue;
672
+ const [activeStopId, setActiveStopId] = useState(null);
673
+ const [draggingStopId, setDraggingStopId] = useState(null);
674
+ const trackRef = useRef(null);
675
+ const rootRef = useRef(null);
676
+ const stops = sortStops(value.stops);
677
+ const activeStop = stops.find((stop) => stop.id === activeStopId) ?? null;
678
+ const canAdd = !disabled && stops.length < maxStops;
679
+ const canRemove = !disabled && stops.length > minStops;
680
+ const emit = useCallback(
681
+ (next) => {
682
+ if (!isControlled) setInnerValue(next);
683
+ onChange?.({ value: next, css: toCssGradient(next) });
684
+ },
685
+ [isControlled, onChange]
686
+ );
687
+ const patchStops = useCallback(
688
+ (nextStops) => {
689
+ if (nextStops === value.stops) return;
690
+ emit({ ...value, stops: nextStops });
691
+ },
692
+ [emit, value]
693
+ );
694
+ const focusMarker = useCallback((stopId) => {
695
+ if (!isWeb) return;
696
+ const node = rootRef.current?.querySelector?.(
697
+ `[data-testid="gradient-stop-${stopId}"]`
698
+ );
699
+ node?.focus?.();
700
+ }, []);
701
+ const closeColorPicker = useCallback(() => {
702
+ const previous = activeStopId;
703
+ setActiveStopId(null);
704
+ if (previous) focusMarker(previous);
705
+ }, [activeStopId, focusMarker]);
706
+ useEffect(() => {
707
+ if (!isWeb || !activeStopId) return void 0;
708
+ const onKey = (event) => {
709
+ if (event.key === "Escape") closeColorPicker();
710
+ };
711
+ document.addEventListener("keydown", onKey);
712
+ return () => document.removeEventListener("keydown", onKey);
713
+ }, [activeStopId, closeColorPicker]);
714
+ const positionFromClientX = useCallback((clientX) => {
715
+ const rect = trackRef.current?.getBoundingClientRect?.();
716
+ if (!rect || !rect.width) return 0;
717
+ return clampPosition((clientX - rect.left) / rect.width * 100);
718
+ }, []);
719
+ const addStop = (position) => {
720
+ const before = value.stops;
721
+ const after = addStopAt(before, position, maxStops);
722
+ if (after === before) return;
723
+ patchStops(after);
724
+ const added = after.find(
725
+ (stop) => !before.some((existing) => existing.id === stop.id)
726
+ );
727
+ if (added) setActiveStopId(added.id);
728
+ };
729
+ const handleTrackPress = (event) => {
730
+ if (!canAdd) return;
731
+ addStop(positionFromClientX(event.clientX));
732
+ };
733
+ const handleAddStopPress = () => {
734
+ if (!canAdd) return;
735
+ const sorted = sortStops(value.stops);
736
+ let position = 50;
737
+ let widest = -1;
738
+ for (let index2 = 0; index2 < sorted.length - 1; index2 += 1) {
739
+ const gap = sorted[index2 + 1].position - sorted[index2].position;
740
+ if (gap > widest) {
741
+ widest = gap;
742
+ position = sorted[index2].position + gap / 2;
743
+ }
744
+ }
745
+ addStop(position);
746
+ };
747
+ const handleRemoveStop = (stopId) => {
748
+ const after = removeStop(value.stops, stopId, minStops);
749
+ if (after === value.stops) return;
750
+ if (activeStopId === stopId) setActiveStopId(null);
751
+ patchStops(after);
752
+ };
753
+ const handleDragStart = (stop) => (event) => {
754
+ if (disabled) return;
755
+ event.preventDefault();
756
+ event.stopPropagation();
757
+ const startY = event.clientY;
758
+ setDraggingStopId(stop.id);
759
+ setActiveStopId(stop.id);
760
+ let latest = value.stops;
761
+ let removed = false;
762
+ const onMouseMove = (moveEvent) => {
763
+ if (removed) return;
764
+ if (Math.abs(moveEvent.clientY - startY) > DRAG_OFF_THRESHOLD) {
765
+ const after = removeStop(latest, stop.id, minStops);
766
+ if (after !== latest) {
767
+ latest = after;
768
+ removed = true;
769
+ patchStops(after);
770
+ }
771
+ return;
772
+ }
773
+ latest = updateStop(latest, stop.id, {
774
+ position: positionFromClientX(moveEvent.clientX)
775
+ });
776
+ patchStops(latest);
777
+ };
778
+ const onMouseUp = () => {
779
+ setDraggingStopId(null);
780
+ if (removed) setActiveStopId(null);
781
+ document.removeEventListener("mousemove", onMouseMove);
782
+ document.removeEventListener("mouseup", onMouseUp);
783
+ };
784
+ document.addEventListener("mousemove", onMouseMove);
785
+ document.addEventListener("mouseup", onMouseUp);
786
+ };
787
+ const handleMarkerKeyDown = (stop) => (event) => {
788
+ if (disabled) return;
789
+ const step = event.shiftKey ? 10 : 1;
790
+ let handled = true;
791
+ switch (event.key) {
792
+ case "ArrowRight":
793
+ case "ArrowUp":
794
+ patchStops(
795
+ updateStop(value.stops, stop.id, { position: stop.position + step })
796
+ );
797
+ break;
798
+ case "ArrowLeft":
799
+ case "ArrowDown":
800
+ patchStops(
801
+ updateStop(value.stops, stop.id, { position: stop.position - step })
802
+ );
803
+ break;
804
+ case "Home":
805
+ patchStops(updateStop(value.stops, stop.id, { position: 0 }));
806
+ break;
807
+ case "End":
808
+ patchStops(updateStop(value.stops, stop.id, { position: 100 }));
809
+ break;
810
+ case "Enter":
811
+ case " ":
812
+ setActiveStopId((current) => current === stop.id ? null : stop.id);
813
+ break;
814
+ case "Delete":
815
+ case "Backspace":
816
+ handleRemoveStop(stop.id);
817
+ break;
818
+ default:
819
+ handled = false;
820
+ }
821
+ if (handled) event.preventDefault();
822
+ };
823
+ const typeOptions = gradientTypes.map((type) => ({
824
+ label: typeLabels?.[type] ?? DEFAULT_TYPE_LABELS[type],
825
+ value: type
826
+ }));
827
+ const showAngle = supportsAngle(value.type);
828
+ return /* @__PURE__ */ jsxs2(
829
+ Box,
830
+ {
831
+ ref: rootRef,
832
+ testID,
833
+ "data-testid": testID,
834
+ backgroundColor: theme.colors.layer.float,
835
+ borderRadius: theme.shape.contextMenu.lg.borderRadius,
836
+ padding: 16,
837
+ gap: 12,
838
+ width: 344,
839
+ style: { boxShadow: "0px 4px 16px 0px rgba(7, 7, 8, 0.1)" },
840
+ children: [
841
+ /* @__PURE__ */ jsxs2(Box, { flexDirection: "row", gap: 8, alignItems: "center", children: [
842
+ /* @__PURE__ */ jsx5(Box, { flex: 1, role: "group", "aria-label": typeAriaLabel, children: /* @__PURE__ */ jsx5(
843
+ Select,
844
+ {
845
+ ...themeProps,
846
+ size: "sm",
847
+ options: typeOptions,
848
+ value: value.type,
849
+ disabled,
850
+ onChange: (next) => emit({ ...value, type: next })
851
+ }
852
+ ) }),
853
+ showAngle && /* @__PURE__ */ jsx5(Box, { width: 88, children: /* @__PURE__ */ jsx5(
854
+ Input,
855
+ {
856
+ ...themeProps,
857
+ size: "sm",
858
+ inputMode: "numeric",
859
+ disabled,
860
+ "aria-label": angleAriaLabel,
861
+ iconLeft: /* @__PURE__ */ jsx5(AngleGlyph, {}),
862
+ value: `${Math.round(value.angle)}`,
863
+ onChange: (event) => {
864
+ const parsed = parseInt(event.target.value, 10);
865
+ emit({
866
+ ...value,
867
+ angle: Number.isNaN(parsed) ? 0 : (parsed % 360 + 360) % 360
868
+ });
869
+ }
870
+ }
871
+ ) })
872
+ ] }),
873
+ /* @__PURE__ */ jsx5(
874
+ Box,
875
+ {
876
+ height: TRACK_HEIGHT,
877
+ justifyContent: "center",
878
+ paddingHorizontal: MARKER_INSET,
879
+ children: /* @__PURE__ */ jsxs2(
880
+ Box,
881
+ {
882
+ ref: trackRef,
883
+ width: "100%",
884
+ height: TRACK_HEIGHT,
885
+ position: "relative",
886
+ cursor: canAdd ? "copy" : "default",
887
+ onMouseDown: isWeb && canAdd ? handleTrackPress : void 0,
888
+ children: [
889
+ /* @__PURE__ */ jsx5(
890
+ Box,
891
+ {
892
+ testID: "gradient-track",
893
+ "data-testid": "gradient-track",
894
+ "aria-hidden": true,
895
+ position: "absolute",
896
+ width: "100%",
897
+ height: TRACK_HEIGHT,
898
+ borderRadius: 999,
899
+ style: {
900
+ top: 0,
901
+ left: 0,
902
+ backgroundImage: toPreviewCssGradient(value)
903
+ }
904
+ }
905
+ ),
906
+ stops.map((stop, index2) => /* @__PURE__ */ jsx5(
907
+ GradientStopMarker,
908
+ {
909
+ stop,
910
+ index: index2 + 1,
911
+ disabled,
912
+ active: activeStopId === stop.id || draggingStopId === stop.id,
913
+ onDragStart: handleDragStart(stop),
914
+ onKeyDown: handleMarkerKeyDown(stop),
915
+ themeMode,
916
+ themeProductContext
917
+ },
918
+ stop.id
919
+ ))
920
+ ]
921
+ }
922
+ )
923
+ }
924
+ ),
925
+ /* @__PURE__ */ jsxs2(
926
+ Box,
927
+ {
928
+ flexDirection: "row",
929
+ alignItems: "center",
930
+ justifyContent: "space-between",
931
+ children: [
932
+ /* @__PURE__ */ jsx5(Text, { color: contentColors.primary, fontSize: 14, children: stopsLabel }),
933
+ /* @__PURE__ */ jsx5(
934
+ IconButton,
935
+ {
936
+ ...themeProps,
937
+ size: "sm",
938
+ variant: "tertiary",
939
+ tone: "mono",
940
+ disabled: !canAdd,
941
+ onPress: handleAddStopPress,
942
+ icon: /* @__PURE__ */ jsx5(PlusGlyph, {}),
943
+ "aria-label": "Add stop"
944
+ }
945
+ )
946
+ ]
947
+ }
948
+ ),
949
+ /* @__PURE__ */ jsx5(Box, { gap: 8, children: stops.map((stop, index2) => /* @__PURE__ */ jsxs2(
950
+ Box,
951
+ {
952
+ flexDirection: "row",
953
+ alignItems: "center",
954
+ gap: 8,
955
+ testID: `gradient-stop-row-${stop.id}`,
956
+ "data-testid": `gradient-stop-row-${stop.id}`,
957
+ children: [
958
+ /* @__PURE__ */ jsx5(Box, { width: 72, children: /* @__PURE__ */ jsx5(
959
+ Input,
960
+ {
961
+ ...themeProps,
962
+ size: "sm",
963
+ inputMode: "numeric",
964
+ disabled,
965
+ "aria-label": `Stop ${index2 + 1} position percent`,
966
+ iconRight: /* @__PURE__ */ jsx5(PercentGlyph, {}),
967
+ value: `${stop.position}`,
968
+ onChange: (event) => {
969
+ const parsed = parseInt(event.target.value, 10);
970
+ if (Number.isNaN(parsed)) return;
971
+ patchStops(
972
+ updateStop(value.stops, stop.id, { position: parsed })
973
+ );
974
+ }
975
+ }
976
+ ) }),
977
+ /* @__PURE__ */ jsx5(Box, { flex: 1, children: /* @__PURE__ */ jsx5(
978
+ InputColor,
979
+ {
980
+ size: "sm",
981
+ type: "color-hex",
982
+ transparency: true,
983
+ "aria-label": `Stop ${index2 + 1} colour`,
984
+ testID: `gradient-stop-color-${stop.id}`,
985
+ value: toHexa(stop.color, stop.opacity),
986
+ onChange: (next) => {
987
+ const { color, opacity } = fromHexa(next);
988
+ patchStops(
989
+ updateStop(value.stops, stop.id, { color, opacity })
990
+ );
991
+ },
992
+ themeMode,
993
+ themeProductContext
994
+ }
995
+ ) }),
996
+ /* @__PURE__ */ jsx5(
997
+ IconButton,
998
+ {
999
+ ...themeProps,
1000
+ size: "sm",
1001
+ variant: "tertiary",
1002
+ tone: "mono",
1003
+ disabled: !canRemove,
1004
+ onPress: () => handleRemoveStop(stop.id),
1005
+ icon: /* @__PURE__ */ jsx5(MinusGlyph, {}),
1006
+ "aria-label": `Remove stop ${index2 + 1}`
1007
+ }
1008
+ )
1009
+ ]
1010
+ },
1011
+ stop.id
1012
+ )) }),
1013
+ activeStop && !disabled && /* @__PURE__ */ jsx5(
1014
+ ColorPicker,
1015
+ {
1016
+ alpha: true,
1017
+ colorFormat: "hex",
1018
+ value: toHexa(activeStop.color, activeStop.opacity),
1019
+ onChange: ({
1020
+ valueInInitialFormat
1021
+ }) => {
1022
+ const { color, opacity } = fromHexa(valueInInitialFormat);
1023
+ patchStops(
1024
+ updateStop(value.stops, activeStop.id, { color, opacity })
1025
+ );
1026
+ },
1027
+ themeMode,
1028
+ themeProductContext
1029
+ }
1030
+ )
1031
+ ]
1032
+ }
1033
+ );
1034
+ };
1035
+ GradientPicker.displayName = "GradientPicker";
1036
+ export {
1037
+ DEFAULT_GRADIENT,
1038
+ GradientPicker,
1039
+ GradientStopMarker,
1040
+ addStopAt,
1041
+ interpolateColorAt,
1042
+ removeStop,
1043
+ sortStops,
1044
+ supportsAngle,
1045
+ toCssGradient,
1046
+ toPreviewCssGradient,
1047
+ updateStop
1048
+ };
1049
+ //# sourceMappingURL=index.mjs.map