@coinbase/cds-web 9.7.1 → 9.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,18 @@ All notable changes to this project will be documented in this file.
8
8
 
9
9
  <!-- template-start -->
10
10
 
11
+ ## 9.8.0 (7/23/2026 PST)
12
+
13
+ #### 🚀 Updates
14
+
15
+ - Feat: add createIcon factory to decouple Icon from a single icon set. [[#786](https://github.com/coinbase/cds/pull/786)]
16
+
17
+ ## 9.7.2 (7/23/2026 PST)
18
+
19
+ #### 🐞 Fixes
20
+
21
+ - Fix: adjust select placeholder fallback. [[#805](https://github.com/coinbase/cds/pull/805)]
22
+
11
23
  ## 9.7.1 ((7/22/2026, 01:58 PM PST))
12
24
 
13
25
  This is an artificial version bump with no new change.
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultSelectControl.d.ts","sourceRoot":"","sources":["../../../src/alpha/select/DefaultSelectControl.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyD,MAAM,OAAO,CAAC;AAgB9E,OAAO,EAEL,KAAK,kBAAkB,EAEvB,KAAK,UAAU,EAChB,MAAM,UAAU,CAAC;AA4BlB,KAAK,wBAAwB,GAAG,CAC9B,IAAI,SAAS,UAAU,EACvB,iBAAiB,SAAS,MAAM,GAAG,MAAM,EAEzC,KAAK,EAAE,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,CAAC,GAAG;IACnD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;CAC9B,KACE,KAAK,CAAC,YAAY,CAAC;AAqgBxB,eAAO,MAAM,oBAAoB,EAAoC,wBAAwB,CAAC"}
1
+ {"version":3,"file":"DefaultSelectControl.d.ts","sourceRoot":"","sources":["../../../src/alpha/select/DefaultSelectControl.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyD,MAAM,OAAO,CAAC;AAgB9E,OAAO,EAEL,KAAK,kBAAkB,EAEvB,KAAK,UAAU,EAChB,MAAM,UAAU,CAAC;AA4BlB,KAAK,wBAAwB,GAAG,CAC9B,IAAI,SAAS,UAAU,EACvB,iBAAiB,SAAS,MAAM,GAAG,MAAM,EAEzC,KAAK,EAAE,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,CAAC,GAAG;IACnD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;CAC9B,KACE,KAAK,CAAC,YAAY,CAAC;AA4gBxB,eAAO,MAAM,oBAAoB,EAAoC,wBAAwB,CAAC"}
@@ -1,62 +1,13 @@
1
- import React from 'react';
2
1
  import type { IconName } from '@coinbase/cds-common/types/IconName';
3
- import type { IconSize, IconSourcePixelSize } from '@coinbase/cds-common/types/IconSize';
4
- import type { SharedProps } from '@coinbase/cds-common/types/SharedProps';
5
- import { type BoxBaseProps, type BoxDefaultElement, type BoxProps } from '../layout/Box';
6
- export type IconBaseProps = SharedProps &
7
- Pick<
8
- BoxBaseProps,
9
- | 'padding'
10
- | 'paddingX'
11
- | 'paddingY'
12
- | 'paddingTop'
13
- | 'paddingEnd'
14
- | 'paddingBottom'
15
- | 'paddingStart'
16
- > & {
17
- /**
18
- * Size for a given icon.
19
- * @default m
20
- */
21
- size?: IconSize;
22
- /** Name of the icon, as defined in Figma. */
23
- name: IconName;
24
- /**
25
- * Fallback element to render if unable to find an icon with matching name
26
- * @default null
27
- * */
28
- fallback?: null | React.ReactNode;
29
- /**
30
- * Toggles the active and inactive state of the navigation icon
31
- * @default false
32
- */
33
- active?: boolean;
34
- /**
35
- * @deprecated Use `style`, `styles.root`, `className`, `classNames.root`, or the `color` prop to customize icon color. This will be removed in a future major release.
36
- * @deprecationExpectedRemoval v10
37
- */
38
- dangerouslySetColor?: string;
39
- };
40
- export type IconProps = IconBaseProps &
41
- BoxProps<BoxDefaultElement> & {
42
- /** Custom inline styles for individual elements of the Icon component */
43
- styles?: {
44
- /** Outer Box wrapper element */
45
- root?: React.CSSProperties;
46
- /** Inner icon glyph element */
47
- icon?: React.CSSProperties;
48
- };
49
- /** Custom class names for individual elements of the Icon component */
50
- classNames?: {
51
- /** Outer Box wrapper element */
52
- root?: string;
53
- /** Inner icon glyph element */
54
- icon?: string;
55
- };
56
- };
57
- declare const getIconSourceSize: (iconSize: number) => IconSourcePixelSize;
58
- export declare const Icon: React.NamedExoticComponent<
59
- Omit<IconProps, 'ref'> & React.RefAttributes<HTMLElement>
2
+ import {
3
+ getIconSourceSize,
4
+ type IconBaseProps as IconBasePropsGeneric,
5
+ type IconProps as IconPropsGeneric,
6
+ } from './createIcon';
7
+ export type IconBaseProps = IconBasePropsGeneric<IconName>;
8
+ export type IconProps = IconPropsGeneric<IconName>;
9
+ export declare const Icon: import('react').NamedExoticComponent<
10
+ Omit<IconPropsGeneric<IconName>, 'ref'> & import('react').RefAttributes<HTMLElement>
60
11
  >;
61
12
  export { getIconSourceSize };
62
13
  //# sourceMappingURL=Icon.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Icon.d.ts","sourceRoot":"","sources":["../../src/icons/Icon.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoC,MAAM,OAAO,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,KAAK,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AACzF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAC;AAS1E,OAAO,EAAO,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAI9F,MAAM,MAAM,aAAa,GAAG,WAAW,GACrC,IAAI,CACF,YAAY,EACV,SAAS,GACT,UAAU,GACV,UAAU,GACV,YAAY,GACZ,YAAY,GACZ,eAAe,GACf,cAAc,CACjB,GAAG;IACF;;;OAGG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,6CAA6C;IAC7C,IAAI,EAAE,QAAQ,CAAC;IACf;;;SAGK;IACL,QAAQ,CAAC,EAAE,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC;IAClC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEJ,MAAM,MAAM,SAAS,GAAG,aAAa,GACnC,QAAQ,CAAC,iBAAiB,CAAC,GAAG;IAC5B,yEAAyE;IACzE,MAAM,CAAC,EAAE;QACP,gCAAgC;QAChC,IAAI,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;QAC3B,+BAA+B;QAC/B,IAAI,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;KAC5B,CAAC;IACF,uEAAuE;IACvE,UAAU,CAAC,EAAE;QACX,gCAAgC;QAChC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,+BAA+B;QAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH,CAAC;AA2CJ,QAAA,MAAM,iBAAiB,GAAI,UAAU,MAAM,KAAG,mBAI7C,CAAC;AAEF,eAAO,MAAM,IAAI,uFAyEhB,CAAC;AAIF,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
1
+ {"version":3,"file":"Icon.d.ts","sourceRoot":"","sources":["../../src/icons/Icon.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AAGpE,OAAO,EAGL,iBAAiB,EACjB,KAAK,aAAa,IAAI,oBAAoB,EAC1C,KAAK,SAAS,IAAI,gBAAgB,EACnC,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,aAAa,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;AAC3D,MAAM,MAAM,SAAS,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;AAEnD,eAAO,MAAM,IAAI,4HAGf,CAAC;AAEH,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
@@ -0,0 +1,113 @@
1
+ import React from 'react';
2
+ import type { IconSize, IconSourcePixelSize } from '@coinbase/cds-common/types/IconSize';
3
+ import type { SharedProps } from '@coinbase/cds-common/types/SharedProps';
4
+ import { type BoxBaseProps, type BoxDefaultElement, type BoxProps } from '../layout/Box';
5
+ /** Default font family for the CDS icon glyph font. */
6
+ export declare const DEFAULT_ICON_FONT_FAMILY = 'CoinbaseIcons';
7
+ /**
8
+ * Shape of the glyph map an icon set must provide. Keys are
9
+ * `${name}-${sourcePixelSize}-${'active' | 'inactive'}` and values are the
10
+ * single Unicode character rendered in the icon font.
11
+ */
12
+ export type GlyphMap<Name extends string> = Record<
13
+ `${Name}-${IconSourcePixelSize}-${'active' | 'inactive'}`,
14
+ string
15
+ >;
16
+ /** Arguments passed to a glyph resolver to look up an icon's glyph. */
17
+ export type IconGlyphResolverArgs<Name extends string> = {
18
+ /** Glyph map bound to this icon set. */
19
+ glyphMap: GlyphMap<Name>;
20
+ /** Icon name requested via the `name` prop. */
21
+ name: Name;
22
+ /** Size token requested via the `size` prop. */
23
+ size: IconSize;
24
+ /** Resolved pixel size from the theme for the requested `size` token. */
25
+ pixelSize: number;
26
+ /** Whether the active variant was requested. */
27
+ active: boolean;
28
+ };
29
+ /** Configuration used to bind an icon set to a typed `Icon` component. */
30
+ export type CreateIconConfig<Name extends string> = {
31
+ /** Generated glyph map for this icon set. */
32
+ glyphMap: GlyphMap<Name>;
33
+ /**
34
+ * `@font-face` family name registered by the icon set's font.
35
+ * @default 'CoinbaseIcons'
36
+ */
37
+ fontFamily?: string;
38
+ /**
39
+ * Resolves the glyph to render for an icon from the glyph map. Override to
40
+ * support a custom key format or size model. Defaults to the CDS scheme:
41
+ * `${name}-${sourceSize}-${'active' | 'inactive'}`, where `sourceSize` is
42
+ * `12`, `16`, or `24`.
43
+ */
44
+ getGlyph?: (args: IconGlyphResolverArgs<Name>) => string | undefined;
45
+ };
46
+ export type IconBaseProps<Name extends string = string> = SharedProps &
47
+ Pick<
48
+ BoxBaseProps,
49
+ | 'padding'
50
+ | 'paddingX'
51
+ | 'paddingY'
52
+ | 'paddingTop'
53
+ | 'paddingEnd'
54
+ | 'paddingBottom'
55
+ | 'paddingStart'
56
+ > & {
57
+ /**
58
+ * Size for a given icon.
59
+ * @default m
60
+ */
61
+ size?: IconSize;
62
+ /** Name of the icon, as defined in Figma. */
63
+ name: Name;
64
+ /**
65
+ * Fallback element to render if unable to find an icon with matching name
66
+ * @default null
67
+ * */
68
+ fallback?: null | React.ReactNode;
69
+ /**
70
+ * Toggles the active and inactive state of the navigation icon
71
+ * @default false
72
+ */
73
+ active?: boolean;
74
+ /**
75
+ * @deprecated Use `style`, `styles.root`, `className`, `classNames.root`, or the `color` prop to customize icon color. This will be removed in a future major release.
76
+ * @deprecationExpectedRemoval v10
77
+ */
78
+ dangerouslySetColor?: string;
79
+ };
80
+ export type IconProps<Name extends string = string> = IconBaseProps<Name> &
81
+ BoxProps<BoxDefaultElement> & {
82
+ /** Custom inline styles for individual elements of the Icon component */
83
+ styles?: {
84
+ /** Outer Box wrapper element */
85
+ root?: React.CSSProperties;
86
+ /** Inner icon glyph element */
87
+ icon?: React.CSSProperties;
88
+ };
89
+ /** Custom class names for individual elements of the Icon component */
90
+ classNames?: {
91
+ /** Outer Box wrapper element */
92
+ root?: string;
93
+ /** Inner icon glyph element */
94
+ icon?: string;
95
+ };
96
+ };
97
+ declare const getIconSourceSize: (iconSize: number) => IconSourcePixelSize;
98
+ /**
99
+ * Creates a typed `Icon` component bound to a specific icon set (glyph map,
100
+ * font family, and name union). The default CDS `Icon` is created from this
101
+ * factory; consumers with their own icon package can create their own typed
102
+ * icon component that reuses all of the CDS rendering, accessibility, and
103
+ * theming behavior.
104
+ */
105
+ export declare function createIcon<Name extends string>({
106
+ glyphMap,
107
+ fontFamily,
108
+ getGlyph,
109
+ }: CreateIconConfig<Name>): React.NamedExoticComponent<
110
+ Omit<IconProps<Name>, 'ref'> & React.RefAttributes<HTMLElement>
111
+ >;
112
+ export { getIconSourceSize };
113
+ //# sourceMappingURL=createIcon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createIcon.d.ts","sourceRoot":"","sources":["../../src/icons/createIcon.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoC,MAAM,OAAO,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AACzF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAC;AAQ1E,OAAO,EAAO,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAI9F,uDAAuD;AACvD,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AAExD;;;;GAIG;AACH,MAAM,MAAM,QAAQ,CAAC,IAAI,SAAS,MAAM,IAAI,MAAM,CAChD,GAAG,IAAI,IAAI,mBAAmB,IAAI,QAAQ,GAAG,UAAU,EAAE,EACzD,MAAM,CACP,CAAC;AAEF,uEAAuE;AACvE,MAAM,MAAM,qBAAqB,CAAC,IAAI,SAAS,MAAM,IAAI;IACvD,wCAAwC;IACxC,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzB,+CAA+C;IAC/C,IAAI,EAAE,IAAI,CAAC;IACX,gDAAgD;IAChD,IAAI,EAAE,QAAQ,CAAC;IACf,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,0EAA0E;AAC1E,MAAM,MAAM,gBAAgB,CAAC,IAAI,SAAS,MAAM,IAAI;IAClD,6CAA6C;IAC7C,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,CAAC,IAAI,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC;CACtE,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,IAAI,WAAW,GACnE,IAAI,CACF,YAAY,EACV,SAAS,GACT,UAAU,GACV,UAAU,GACV,YAAY,GACZ,YAAY,GACZ,eAAe,GACf,cAAc,CACjB,GAAG;IACF;;;OAGG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,6CAA6C;IAC7C,IAAI,EAAE,IAAI,CAAC;IACX;;;SAGK;IACL,QAAQ,CAAC,EAAE,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC;IAClC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEJ,MAAM,MAAM,SAAS,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,IAAI,aAAa,CAAC,IAAI,CAAC,GACvE,QAAQ,CAAC,iBAAiB,CAAC,GAAG;IAC5B,yEAAyE;IACzE,MAAM,CAAC,EAAE;QACP,gCAAgC;QAChC,IAAI,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;QAC3B,+BAA+B;QAC/B,IAAI,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;KAC5B,CAAC;IACF,uEAAuE;IACvE,UAAU,CAAC,EAAE;QACX,gCAAgC;QAChC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,+BAA+B;QAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH,CAAC;AA2CJ,QAAA,MAAM,iBAAiB,GAAI,UAAU,MAAM,KAAG,mBAI7C,CAAC;AAcF;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,SAAS,MAAM,EAAE,EAC9C,QAAQ,EACR,UAAqC,EACrC,QAA0B,GAC3B,EAAE,gBAAgB,CAAC,IAAI,CAAC,+FAgGxB;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
@@ -1,4 +1,6 @@
1
1
  export * from './Icon';
2
+ export { createIcon, DEFAULT_ICON_FONT_FAMILY } from './createIcon';
3
+ export type { CreateIconConfig, GlyphMap, IconGlyphResolverArgs } from './createIcon';
2
4
  export * from './LogoMark';
3
5
  export * from './LogoWordmark';
4
6
  export * from './SubBrandLogoMark';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/icons/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/icons/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACpE,YAAY,EAAE,gBAAgB,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACtF,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC"}
@@ -120,19 +120,23 @@ const DefaultSelectControlComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef(
120
120
  });
121
121
  return map;
122
122
  }, [options]);
123
+ const matchedOption = useMemo(() => {
124
+ if (isMultiSelect || value === null || Array.isArray(value)) return undefined;
125
+ return optionsMap.get(value);
126
+ }, [isMultiSelect, optionsMap, value]);
127
+ const isShowingPlaceholder = matchedOption === undefined;
123
128
  const singleValueContent = useMemo(() => {
124
- var _ref3, _ref4, _option$label;
125
- const option = !isMultiSelect ? optionsMap.get(value) : undefined;
126
- const label = (_ref3 = (_ref4 = (_option$label = option === null || option === void 0 ? void 0 : option.label) !== null && _option$label !== void 0 ? _option$label : option === null || option === void 0 ? void 0 : option.description) !== null && _ref4 !== void 0 ? _ref4 : option === null || option === void 0 ? void 0 : option.value) !== null && _ref3 !== void 0 ? _ref3 : placeholder;
127
- return hasValue ? label : placeholder;
128
- }, [hasValue, isMultiSelect, optionsMap, placeholder, value]);
129
+ var _ref3, _matchedOption$label;
130
+ if (!matchedOption) return placeholder;
131
+ return (_ref3 = (_matchedOption$label = matchedOption.label) !== null && _matchedOption$label !== void 0 ? _matchedOption$label : matchedOption.description) !== null && _ref3 !== void 0 ? _ref3 : matchedOption.value;
132
+ }, [matchedOption, placeholder]);
129
133
  const computedControlAccessibilityLabel = useMemo(() => {
130
134
  // For multi-select, set the label to the content of each selected value and the hidden selected options label
131
135
  if (isMultiSelect) {
132
136
  const selectedValues = value.map(v => {
133
- var _ref5, _ref6, _option$label2;
137
+ var _ref4, _ref5, _option$label;
134
138
  const option = optionsMap.get(v);
135
- return (_ref5 = (_ref6 = (_option$label2 = option === null || option === void 0 ? void 0 : option.label) !== null && _option$label2 !== void 0 ? _option$label2 : option === null || option === void 0 ? void 0 : option.description) !== null && _ref6 !== void 0 ? _ref6 : option === null || option === void 0 ? void 0 : option.value) !== null && _ref5 !== void 0 ? _ref5 : v;
139
+ return (_ref4 = (_ref5 = (_option$label = option === null || option === void 0 ? void 0 : option.label) !== null && _option$label !== void 0 ? _option$label : option === null || option === void 0 ? void 0 : option.description) !== null && _ref5 !== void 0 ? _ref5 : option === null || option === void 0 ? void 0 : option.value) !== null && _ref4 !== void 0 ? _ref4 : v;
136
140
  }).slice(0, maxSelectedOptionsToShow).join(', ');
137
141
  return "".concat(accessibilityLabel, ", ").concat(value.length > 0 ? selectedValues : placeholder !== null && placeholder !== void 0 ? placeholder : '').concat(value.length > maxSelectedOptionsToShow ? ', ' + hiddenSelectedOptionsLabel : '');
138
142
  }
@@ -206,7 +210,7 @@ const DefaultSelectControlComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef(
206
210
  flexWrap: "wrap",
207
211
  gap: 1,
208
212
  children: [optionsToShow.map((option, index) => {
209
- var _option$value, _ref7, _ref8, _option$label3;
213
+ var _option$value, _ref6, _ref7, _option$label2;
210
214
  const accessibilityLabel = typeof option.label === 'string' ? option.label : typeof option.description === 'string' ? option.description : (_option$value = option.value) !== null && _option$value !== void 0 ? _option$value : '';
211
215
  return /*#__PURE__*/_jsx(InputChip, {
212
216
  compact: true,
@@ -225,7 +229,7 @@ const DefaultSelectControlComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef(
225
229
  flexShrink: 1,
226
230
  font: "label1",
227
231
  overflow: "truncate",
228
- children: (_ref7 = (_ref8 = (_option$label3 = option.label) !== null && _option$label3 !== void 0 ? _option$label3 : option.description) !== null && _ref8 !== void 0 ? _ref8 : option.value) !== null && _ref7 !== void 0 ? _ref7 : ''
232
+ children: (_ref6 = (_ref7 = (_option$label2 = option.label) !== null && _option$label2 !== void 0 ? _option$label2 : option.description) !== null && _ref7 !== void 0 ? _ref7 : option.value) !== null && _ref6 !== void 0 ? _ref6 : ''
229
233
  })
230
234
  }, option.value);
231
235
  }), value.length - maxSelectedOptionsToShow > 0 && /*#__PURE__*/_jsx(InputChip, {
@@ -239,7 +243,7 @@ const DefaultSelectControlComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef(
239
243
  }
240
244
  return typeof singleValueContent === 'string' ? /*#__PURE__*/_jsx(Text, {
241
245
  as: "p",
242
- color: hasValue ? 'fg' : 'fgMuted',
246
+ color: isShowingPlaceholder ? 'fgMuted' : 'fg',
243
247
  display: "block",
244
248
  font: font,
245
249
  overflow: "truncate",
@@ -247,7 +251,7 @@ const DefaultSelectControlComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef(
247
251
  width: "100%",
248
252
  children: singleValueContent
249
253
  }) : singleValueContent;
250
- }, [hasValue, isMultiSelect, singleValueContent, font, align, value, maxSelectedOptionsToShow, hiddenSelectedOptionsLabel, optionsMap, removeSelectedOptionAccessibilityLabel, handleUnselectValue, isInteractionBlocked]);
254
+ }, [hasValue, isMultiSelect, isShowingPlaceholder, singleValueContent, font, align, value, maxSelectedOptionsToShow, hiddenSelectedOptionsLabel, optionsMap, removeSelectedOptionAccessibilityLabel, handleUnselectValue, isInteractionBlocked]);
251
255
  const inputNode = useMemo(() => /*#__PURE__*/_jsxs(Pressable, {
252
256
  ref: controlPressableRef,
253
257
  noScaleOnPress: true,
package/esm/icons/Icon.js CHANGED
@@ -1,86 +1,7 @@
1
- const _excluded = ["accessibilityLabel", "color", "dangerouslySetColor", "fallback", "name", "size", "testID", "className", "classNames", "style", "styles", "active"];
2
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
- function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
5
- function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
6
- function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
7
- function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
8
- function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
9
- import React, { forwardRef, memo, useMemo } from 'react';
10
1
  import { glyphMap } from '@coinbase/cds-icons/glyphMap';
11
- import { isDevelopment } from '@coinbase/cds-utils/env';
12
- import { cx } from '../cx';
13
- import { useComponentConfig } from '../hooks/useComponentConfig';
14
- import { useTheme } from '../hooks/useTheme';
15
- import { Box } from '../layout/Box';
16
- import { jsx as _jsx } from "react/jsx-runtime";
17
- const COMPONENT_STATIC_CLASSNAME = 'cds-Icon';
18
- const iconCss = "iconCss-ijgxn1p";
19
- const sizeCss = {
20
- xs: "xs-x1o2vka7",
21
- s: "s-smpvpdm",
22
- m: "m-m1j7b465",
23
- l: "l-l1bri864"
24
- };
25
- const getIconSourceSize = iconSize => {
26
- if (iconSize <= 12) return 12;
27
- if (iconSize <= 16) return 16;
28
- return 24;
29
- };
30
- export const Icon = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_props, ref) => {
31
- const mergedProps = useComponentConfig('Icon', _props);
32
- const {
33
- accessibilityLabel,
34
- color = 'fgPrimary',
35
- dangerouslySetColor,
36
- fallback = null,
37
- name,
38
- size = 'm',
39
- testID,
40
- className,
41
- classNames,
42
- style,
43
- styles,
44
- active
45
- } = mergedProps,
46
- props = _objectWithoutProperties(mergedProps, _excluded);
47
- const theme = useTheme();
48
- const iconSize = theme.iconSize[size];
49
- const sourceSize = getIconSourceSize(iconSize);
50
- const rootStyle = useMemo(() => _objectSpread(_objectSpread(_objectSpread({}, dangerouslySetColor ? {
51
- color: dangerouslySetColor
52
- } : {}), style), styles === null || styles === void 0 ? void 0 : styles.root), [dangerouslySetColor, style, styles === null || styles === void 0 ? void 0 : styles.root]);
53
- const iconName = "".concat(name, "-").concat(sourceSize, "-").concat(active ? 'active' : 'inactive');
54
- const glyph = glyphMap[iconName];
55
- if (glyph === undefined) {
56
- if (isDevelopment()) {
57
- console.error("Unable to find glyph for icon name \"".concat(name, "\" with glyph key \"").concat(iconName, "\""));
58
- }
59
- return fallback;
60
- }
61
- const glyphTestId = testID ? "".concat(testID, "-glyph") : 'icon-base-glyph';
62
- return /*#__PURE__*/_jsx(Box, _objectSpread(_objectSpread({
63
- className: cx(COMPONENT_STATIC_CLASSNAME, className, classNames === null || classNames === void 0 ? void 0 : classNames.root),
64
- color: color,
65
- position: "relative",
66
- style: rootStyle,
67
- testID: testID
68
- }, props), {}, {
69
- children: /*#__PURE__*/_jsx("span", {
70
- ref: ref,
71
- "aria-hidden": !accessibilityLabel,
72
- "aria-label": accessibilityLabel,
73
- className: cx(iconCss, sizeCss[size], classNames === null || classNames === void 0 ? void 0 : classNames.icon),
74
- "data-icon-name": name,
75
- "data-testid": glyphTestId,
76
- role: "img",
77
- style: styles === null || styles === void 0 ? void 0 : styles.icon,
78
- title: accessibilityLabel,
79
- translate: "no",
80
- children: glyph
81
- })
82
- }));
83
- }));
84
- Icon.displayName = 'Icon';
85
- export { getIconSourceSize };
86
- import "./Icon.css";
2
+ import { createIcon, DEFAULT_ICON_FONT_FAMILY, getIconSourceSize } from './createIcon';
3
+ export const Icon = createIcon({
4
+ glyphMap,
5
+ fontFamily: DEFAULT_ICON_FONT_FAMILY
6
+ });
7
+ export { getIconSourceSize };
@@ -0,0 +1,5 @@
1
+ @layer cds{.iconCss-i1t3cbh7{color:currentColor;font-family:var(--cds-icon-font-family,'CoinbaseIcons');font-weight:400;font-style:normal;font-variant:normal;text-rendering:geometricPrecision;line-height:1;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;display:block;-webkit-text-decoration:none;text-decoration:none;}.iconCss-i1t3cbh7 > *{-webkit-transition:fill 150ms ease-in-out;transition:fill 150ms ease-in-out;}
2
+ .xs-xffinnj{width:var(--iconSize-xs);height:var(--iconSize-xs);font-size:var(--iconSize-xs);}
3
+ .s-s1qkbjwr{width:var(--iconSize-s);height:var(--iconSize-s);font-size:var(--iconSize-s);}
4
+ .m-mvegnts{width:var(--iconSize-m);height:var(--iconSize-m);font-size:var(--iconSize-m);}
5
+ .l-lc7jme9{width:var(--iconSize-l);height:var(--iconSize-l);font-size:var(--iconSize-l);}}
@@ -0,0 +1,138 @@
1
+ const _excluded = ["accessibilityLabel", "color", "dangerouslySetColor", "fallback", "name", "size", "testID", "className", "classNames", "style", "styles", "active"];
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
5
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
6
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
7
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
8
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
9
+ import React, { forwardRef, memo, useMemo } from 'react';
10
+ import { isDevelopment } from '@coinbase/cds-utils/env';
11
+ import { cx } from '../cx';
12
+ import { useComponentConfig } from '../hooks/useComponentConfig';
13
+ import { useTheme } from '../hooks/useTheme';
14
+ import { Box } from '../layout/Box';
15
+ import { jsx as _jsx } from "react/jsx-runtime";
16
+ const COMPONENT_STATIC_CLASSNAME = 'cds-Icon';
17
+
18
+ /** Default font family for the CDS icon glyph font. */
19
+ export const DEFAULT_ICON_FONT_FAMILY = 'CoinbaseIcons';
20
+
21
+ /**
22
+ * Shape of the glyph map an icon set must provide. Keys are
23
+ * `${name}-${sourcePixelSize}-${'active' | 'inactive'}` and values are the
24
+ * single Unicode character rendered in the icon font.
25
+ */
26
+
27
+ /** Arguments passed to a glyph resolver to look up an icon's glyph. */
28
+
29
+ /** Configuration used to bind an icon set to a typed `Icon` component. */
30
+
31
+ const iconCss = "iconCss-i1t3cbh7";
32
+ const sizeCss = {
33
+ xs: "xs-xffinnj",
34
+ s: "s-s1qkbjwr",
35
+ m: "m-mvegnts",
36
+ l: "l-lc7jme9"
37
+ };
38
+ const getIconSourceSize = iconSize => {
39
+ if (iconSize <= 12) return 12;
40
+ if (iconSize <= 16) return 16;
41
+ return 24;
42
+ };
43
+
44
+ /** Default glyph resolver using the CDS `${name}-${sourceSize}-${state}` key scheme. */
45
+ const defaultGetGlyph = _ref => {
46
+ let {
47
+ glyphMap,
48
+ name,
49
+ pixelSize,
50
+ active
51
+ } = _ref;
52
+ const sourceSize = getIconSourceSize(pixelSize);
53
+ const key = "".concat(name, "-").concat(sourceSize, "-").concat(active ? 'active' : 'inactive');
54
+ return glyphMap[key];
55
+ };
56
+
57
+ /**
58
+ * Creates a typed `Icon` component bound to a specific icon set (glyph map,
59
+ * font family, and name union). The default CDS `Icon` is created from this
60
+ * factory; consumers with their own icon package can create their own typed
61
+ * icon component that reuses all of the CDS rendering, accessibility, and
62
+ * theming behavior.
63
+ */
64
+ export function createIcon(_ref2) {
65
+ let {
66
+ glyphMap,
67
+ fontFamily = DEFAULT_ICON_FONT_FAMILY,
68
+ getGlyph = defaultGetGlyph
69
+ } = _ref2;
70
+ const Icon = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_props, ref) => {
71
+ const mergedProps = useComponentConfig('Icon', _props);
72
+ const {
73
+ accessibilityLabel,
74
+ color = 'fgPrimary',
75
+ dangerouslySetColor,
76
+ fallback = null,
77
+ name,
78
+ size = 'm',
79
+ testID,
80
+ className,
81
+ classNames,
82
+ style,
83
+ styles,
84
+ active
85
+ } = mergedProps,
86
+ props = _objectWithoutProperties(mergedProps, _excluded);
87
+ const theme = useTheme();
88
+ const iconSize = theme.iconSize[size];
89
+ const rootStyle = useMemo(() => _objectSpread(_objectSpread(_objectSpread({}, dangerouslySetColor ? {
90
+ color: dangerouslySetColor
91
+ } : {}), style), styles === null || styles === void 0 ? void 0 : styles.root), [dangerouslySetColor, style, styles === null || styles === void 0 ? void 0 : styles.root]);
92
+
93
+ // Only override the font-family CSS variable when a custom font is bound;
94
+ // the default is applied by the static Linaria block's fallback value.
95
+ const iconStyle = useMemo(() => fontFamily === DEFAULT_ICON_FONT_FAMILY ? styles === null || styles === void 0 ? void 0 : styles.icon : _objectSpread({
96
+ '--cds-icon-font-family': fontFamily
97
+ }, styles === null || styles === void 0 ? void 0 : styles.icon), [styles === null || styles === void 0 ? void 0 : styles.icon]);
98
+ const glyph = getGlyph({
99
+ glyphMap,
100
+ name,
101
+ size,
102
+ pixelSize: iconSize,
103
+ active: Boolean(active)
104
+ });
105
+ if (glyph === undefined) {
106
+ if (isDevelopment()) {
107
+ console.error("Unable to find glyph for icon \"".concat(name, "\" at size \"").concat(size, "\""));
108
+ }
109
+ return fallback;
110
+ }
111
+ const glyphTestId = testID ? "".concat(testID, "-glyph") : 'icon-base-glyph';
112
+ return /*#__PURE__*/_jsx(Box, _objectSpread(_objectSpread({
113
+ className: cx(COMPONENT_STATIC_CLASSNAME, className, classNames === null || classNames === void 0 ? void 0 : classNames.root),
114
+ color: color,
115
+ position: "relative",
116
+ style: rootStyle,
117
+ testID: testID
118
+ }, props), {}, {
119
+ children: /*#__PURE__*/_jsx("span", {
120
+ ref: ref,
121
+ "aria-hidden": !accessibilityLabel,
122
+ "aria-label": accessibilityLabel,
123
+ className: cx(iconCss, sizeCss[size], classNames === null || classNames === void 0 ? void 0 : classNames.icon),
124
+ "data-icon-name": name,
125
+ "data-testid": glyphTestId,
126
+ role: "img",
127
+ style: iconStyle,
128
+ title: accessibilityLabel,
129
+ translate: "no",
130
+ children: glyph
131
+ })
132
+ }));
133
+ }));
134
+ Icon.displayName = 'Icon';
135
+ return Icon;
136
+ }
137
+ export { getIconSourceSize };
138
+ import "./createIcon.css";
@@ -1,4 +1,5 @@
1
1
  export * from './Icon';
2
+ export { createIcon, DEFAULT_ICON_FONT_FAMILY } from './createIcon';
2
3
  export * from './LogoMark';
3
4
  export * from './LogoWordmark';
4
5
  export * from './SubBrandLogoMark';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coinbase/cds-web",
3
- "version": "9.7.1",
3
+ "version": "9.8.0",
4
4
  "description": "Coinbase Design System - Web",
5
5
  "repository": {
6
6
  "type": "git",
@@ -227,7 +227,7 @@
227
227
  "react-dom": "^18.0.0 || ~19.1.2"
228
228
  },
229
229
  "dependencies": {
230
- "@coinbase/cds-common": "^9.7.1",
230
+ "@coinbase/cds-common": "^9.8.0",
231
231
  "@coinbase/cds-icons": "^5.21.0",
232
232
  "@coinbase/cds-illustrations": "^4.44.0",
233
233
  "@coinbase/cds-lottie-files": "^3.3.4",
@@ -1,5 +0,0 @@
1
- @layer cds{.iconCss-ijgxn1p{color:currentColor;font-family:'CoinbaseIcons';font-weight:400;font-style:normal;font-variant:normal;text-rendering:geometricPrecision;line-height:1;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;display:block;-webkit-text-decoration:none;text-decoration:none;}.iconCss-ijgxn1p > *{-webkit-transition:fill 150ms ease-in-out;transition:fill 150ms ease-in-out;}
2
- .xs-x1o2vka7{width:var(--iconSize-xs);height:var(--iconSize-xs);font-size:var(--iconSize-xs);}
3
- .s-smpvpdm{width:var(--iconSize-s);height:var(--iconSize-s);font-size:var(--iconSize-s);}
4
- .m-m1j7b465{width:var(--iconSize-m);height:var(--iconSize-m);font-size:var(--iconSize-m);}
5
- .l-l1bri864{width:var(--iconSize-l);height:var(--iconSize-l);font-size:var(--iconSize-l);}}