@ds-mo/ui 1.1.0 → 1.3.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.
Files changed (107) hide show
  1. package/README.md +82 -3
  2. package/dist/components/ds-badge.js +1 -1
  3. package/dist/components/ds-badge.js.map +1 -1
  4. package/dist/components/ds-banner.js +1 -1
  5. package/dist/components/ds-banner.js.map +1 -1
  6. package/dist/components/ds-bar-nav-action.js +1 -1
  7. package/dist/components/ds-bar-nav.js +1 -1
  8. package/dist/components/ds-bar-nav.js.map +1 -1
  9. package/dist/components/ds-button-group.js +1 -1
  10. package/dist/components/ds-checkbox.js +1 -1
  11. package/dist/components/{ds-label-wrap.d.ts → ds-chip.d.ts} +4 -4
  12. package/dist/components/ds-chip.js +2 -0
  13. package/dist/components/ds-chip.js.map +1 -0
  14. package/dist/components/ds-divider.js +1 -1
  15. package/dist/components/ds-divider.js.map +1 -1
  16. package/dist/components/ds-empty-state.js +1 -1
  17. package/dist/components/ds-fade.js +1 -1
  18. package/dist/components/ds-fade.js.map +1 -1
  19. package/dist/components/ds-field.js +1 -1
  20. package/dist/components/ds-header.js +1 -1
  21. package/dist/components/ds-icon.js +1 -1
  22. package/dist/components/ds-input.js +1 -1
  23. package/dist/components/ds-modal.js +1 -1
  24. package/dist/components/ds-modal.js.map +1 -1
  25. package/dist/components/ds-panel-nav.js +1 -1
  26. package/dist/components/ds-panel-nav.js.map +1 -1
  27. package/dist/components/ds-radio-group.js +1 -1
  28. package/dist/components/ds-radio-group.js.map +1 -1
  29. package/dist/components/ds-scrollbar.js +1 -1
  30. package/dist/components/ds-select.js +1 -1
  31. package/dist/components/ds-sidebar.js +1 -1
  32. package/dist/components/ds-sidebar.js.map +1 -1
  33. package/dist/components/ds-slider.js +1 -1
  34. package/dist/components/ds-slider.js.map +1 -1
  35. package/dist/components/ds-surface.js +1 -1
  36. package/dist/components/ds-tab-group-nav.js +1 -1
  37. package/dist/components/ds-tab-group.js +1 -1
  38. package/dist/components/ds-tab-group.js.map +1 -1
  39. package/dist/components/ds-table.js +1 -1
  40. package/dist/components/ds-table.js.map +1 -1
  41. package/dist/components/ds-tag.js +1 -1
  42. package/dist/components/ds-tag.js.map +1 -1
  43. package/dist/components/ds-text.js +1 -1
  44. package/dist/components/ds-toast-provider.js +1 -1
  45. package/dist/components/ds-toggle-button-group.js +1 -1
  46. package/dist/components/ds-toggle-button.js +1 -1
  47. package/dist/components/ds-toggle.js +1 -1
  48. package/dist/components/ds-tooltip.js +1 -1
  49. package/dist/components/p-B_enucPQ.js +2 -0
  50. package/dist/components/p-B_enucPQ.js.map +1 -0
  51. package/dist/components/p-DOtcc9UU.js +2 -0
  52. package/dist/components/p-DOtcc9UU.js.map +1 -0
  53. package/dist/components/{p-H89PzqGU.js → p-DrDrPeVV.js} +2 -2
  54. package/dist/components/p-OT-Q-NxH.js +2 -0
  55. package/dist/components/p-OT-Q-NxH.js.map +1 -0
  56. package/dist/components/{p-Dd0GrKaF.js → p-U1A-rprN.js} +2 -2
  57. package/dist/components/p-X1N_ggsY.js +2 -0
  58. package/dist/components/p-X1N_ggsY.js.map +1 -0
  59. package/dist/components/p-qfvbc9w-.js +2 -0
  60. package/dist/components/p-qfvbc9w-.js.map +1 -0
  61. package/dist/types/.stencil/Badge/Badge.d.ts +12 -0
  62. package/dist/types/.stencil/Badge/index.d.ts +1 -1
  63. package/dist/types/.stencil/Chip/Chip.d.ts +30 -0
  64. package/dist/types/.stencil/Chip/index.d.ts +2 -0
  65. package/dist/types/.stencil/Divider/Divider.d.ts +14 -0
  66. package/dist/types/.stencil/Fade/Fade.d.ts +17 -2
  67. package/dist/types/.stencil/Fade/index.d.ts +1 -1
  68. package/dist/types/.stencil/Tag/Tag.d.ts +0 -17
  69. package/dist/types/.stencil/Tag/index.d.ts +1 -1
  70. package/dist/types/.stencil/Text/Text.d.ts +2 -0
  71. package/dist/types/components.d.ts +283 -143
  72. package/package.json +12 -5
  73. package/src/angular/index.ts +1 -1
  74. package/src/angular/proxies.ts +51 -51
  75. package/src/react/components.ts +1 -1
  76. package/src/react/ds-chip.ts +35 -0
  77. package/src/react/ds-tag.ts +3 -12
  78. package/src/wc/components/Badge/Badge.tsx +58 -8
  79. package/src/wc/components/Badge/index.ts +1 -1
  80. package/src/wc/components/Banner/Banner.tsx +2 -2
  81. package/src/wc/components/BarNav/BarNav.tsx +7 -1
  82. package/src/wc/components/BarNavAction/BarNavAction.tsx +9 -1
  83. package/src/wc/components/Chip/Chip.tsx +127 -0
  84. package/src/wc/components/Chip/index.ts +1 -0
  85. package/src/wc/components/Divider/Divider.tsx +88 -4
  86. package/src/wc/components/Fade/Fade.tsx +86 -6
  87. package/src/wc/components/Fade/index.ts +1 -1
  88. package/src/wc/components/PanelNav/PanelNav.tsx +17 -1
  89. package/src/wc/components/TabGroup/TabGroup.tsx +9 -1
  90. package/src/wc/components/TabGroupNav/TabGroupNav.tsx +9 -1
  91. package/src/wc/components/Tag/Tag.tsx +3 -70
  92. package/src/wc/components/Tag/index.ts +1 -1
  93. package/src/wc/components/Text/Text.tsx +3 -0
  94. package/src/wc/components.d.ts +283 -143
  95. package/dist/components/ds-label-wrap.js +0 -2
  96. package/dist/components/ds-label-wrap.js.map +0 -1
  97. package/dist/components/p-CMyQsk0K.js +0 -2
  98. package/dist/components/p-CMyQsk0K.js.map +0 -1
  99. package/dist/components/p-Vwlmhl4E.js +0 -2
  100. package/dist/components/p-Vwlmhl4E.js.map +0 -1
  101. package/dist/components/p-woooYulR.js +0 -2
  102. package/dist/components/p-woooYulR.js.map +0 -1
  103. package/dist/types/.stencil/LabelWrap/LabelWrap.d.ts +0 -15
  104. package/src/react/ds-label-wrap.ts +0 -26
  105. package/src/wc/components/LabelWrap/LabelWrap.tsx +0 -38
  106. /package/dist/components/{p-H89PzqGU.js.map → p-DrDrPeVV.js.map} +0 -0
  107. /package/dist/components/{p-Dd0GrKaF.js.map → p-U1A-rprN.js.map} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ds-mo/ui",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "CompoMo — composable web components styled with TokoMo design tokens",
5
5
  "keywords": [
6
6
  "web-components",
@@ -62,14 +62,20 @@
62
62
  "test:e2e": "npm run build && playwright test",
63
63
  "test:e2e:install": "playwright install chromium",
64
64
  "build:docs": "stencil build --docs",
65
- "dev": "node scripts/generate-icon-catalog.mjs && stencil build --dev --watch",
66
- "storybook": "stencil build && storybook dev -p 6006",
65
+ "dev": "node scripts/generate-icon-catalog.mjs && stencil build --watch",
66
+ "storybook:ui": "storybook dev -p 6006 --no-open",
67
+ "storybook": "node scripts/storybook-dev.mjs",
67
68
  "storybook:build": "storybook build",
68
69
  "clean": "rm -rf dist",
69
70
  "typecheck": "tsc --noEmit",
70
71
  "lint": "eslint src/",
71
72
  "registry:build": "node scripts/build-registry.mjs",
72
- "mcp": "node scripts/mcp-server.mjs"
73
+ "mcp": "node scripts/mcp-server.mjs",
74
+ "figma:connect:help": "figma connect --help",
75
+ "figma:connect:publish:dry-run": "figma connect publish --dry-run -c figma.config.json --exit-on-unreadable-files",
76
+ "figma:connect:publish": "figma connect publish -c figma.config.json --exit-on-unreadable-files",
77
+ "figma:connect:preview": "figma connect preview -c figma.config.json",
78
+ "typecheck:figma": "tsc -p tsconfig.figma.json --noEmit"
73
79
  },
74
80
  "peerDependencies": {
75
81
  "@ds-mo/icons": "^6.0.0",
@@ -87,8 +93,9 @@
87
93
  },
88
94
  "devDependencies": {
89
95
  "@ds-mo/icons": "^6.0.0",
90
- "@ds-mo/tokens": "^2.9.1",
96
+ "@ds-mo/tokens": "^2.10.0",
91
97
  "@eslint/js": "^10.0.1",
98
+ "@figma/code-connect": "^1.4.8",
92
99
  "@modelcontextprotocol/sdk": "^1.29.0",
93
100
  "@playwright/test": "^1.55.0",
94
101
  "@stencil/angular-output-target": "^1.3.1",
@@ -12,6 +12,7 @@ export const DIRECTIVES = [
12
12
  d.DsButtonGroup,
13
13
  d.DsCard,
14
14
  d.DsCheckbox,
15
+ d.DsChip,
15
16
  d.DsDivider,
16
17
  d.DsEmptyState,
17
18
  d.DsFade,
@@ -19,7 +20,6 @@ export const DIRECTIVES = [
19
20
  d.DsHeader,
20
21
  d.DsIcon,
21
22
  d.DsInput,
22
- d.DsLabelWrap,
23
23
  d.DsLoader,
24
24
  d.DsMenu,
25
25
  d.DsModal,
@@ -36,14 +36,14 @@ export declare interface DsAccordion extends Components.DsAccordion {
36
36
 
37
37
 
38
38
  @ProxyCmp({
39
- inputs: ['count', 'isSelected', 'label']
39
+ inputs: ['background', 'count', 'isSelected', 'label', 'max', 'surface', 'variant']
40
40
  })
41
41
  @Component({
42
42
  selector: 'ds-badge',
43
43
  changeDetection: ChangeDetectionStrategy.OnPush,
44
44
  template: '<ng-content></ng-content>',
45
45
  // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
46
- inputs: ['count', 'isSelected', 'label'],
46
+ inputs: ['background', 'count', 'isSelected', 'label', 'max', 'surface', 'variant'],
47
47
  standalone: false
48
48
  })
49
49
  export class DsBadge {
@@ -297,14 +297,54 @@ export declare interface DsCheckbox extends Components.DsCheckbox {
297
297
 
298
298
 
299
299
  @ProxyCmp({
300
- inputs: ['orientation']
300
+ inputs: ['background', 'contrast', 'elevation', 'inactive', 'intent', 'label', 'maxWidth', 'pressed', 'removable', 'rounded', 'size']
301
+ })
302
+ @Component({
303
+ selector: 'ds-chip',
304
+ changeDetection: ChangeDetectionStrategy.OnPush,
305
+ template: '<ng-content></ng-content>',
306
+ // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
307
+ inputs: ['background', 'contrast', 'elevation', 'inactive', 'intent', { name: 'label', required: true }, 'maxWidth', 'pressed', 'removable', 'rounded', 'size'],
308
+ outputs: ['dsRemove', 'dsClick', 'dsPressedChange'],
309
+ standalone: false
310
+ })
311
+ export class DsChip {
312
+ protected el: HTMLDsChipElement;
313
+ @Output() dsRemove = new EventEmitter<CustomEvent<void>>();
314
+ @Output() dsClick = new EventEmitter<CustomEvent<void>>();
315
+ @Output() dsPressedChange = new EventEmitter<CustomEvent<boolean>>();
316
+ constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
317
+ c.detach();
318
+ this.el = r.nativeElement;
319
+ }
320
+ }
321
+
322
+
323
+ export declare interface DsChip extends Components.DsChip {
324
+ /**
325
+ * Fired when the remove button is clicked.
326
+ */
327
+ dsRemove: EventEmitter<CustomEvent<void>>;
328
+ /**
329
+ * Fired when an interactive chip is clicked.
330
+ */
331
+ dsClick: EventEmitter<CustomEvent<void>>;
332
+ /**
333
+ * Fired when the pressed state toggles.
334
+ */
335
+ dsPressedChange: EventEmitter<CustomEvent<boolean>>;
336
+ }
337
+
338
+
339
+ @ProxyCmp({
340
+ inputs: ['inset', 'length', 'orientation', 'semantic', 'surface']
301
341
  })
302
342
  @Component({
303
343
  selector: 'ds-divider',
304
344
  changeDetection: ChangeDetectionStrategy.OnPush,
305
345
  template: '<ng-content></ng-content>',
306
346
  // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
307
- inputs: ['orientation'],
347
+ inputs: ['inset', 'length', 'orientation', 'semantic', 'surface'],
308
348
  standalone: false
309
349
  })
310
350
  export class DsDivider {
@@ -343,14 +383,14 @@ export declare interface DsEmptyState extends Components.DsEmptyState {}
343
383
 
344
384
 
345
385
  @ProxyCmp({
346
- inputs: ['background', 'height', 'side']
386
+ inputs: ['background', 'height', 'side', 'size', 'surface', 'visible']
347
387
  })
348
388
  @Component({
349
389
  selector: 'ds-fade',
350
390
  changeDetection: ChangeDetectionStrategy.OnPush,
351
391
  template: '<ng-content></ng-content>',
352
392
  // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
353
- inputs: ['background', 'height', { name: 'side', required: true }],
393
+ inputs: ['background', 'height', 'side', 'size', 'surface', 'visible'],
354
394
  standalone: false
355
395
  })
356
396
  export class DsFade {
@@ -466,29 +506,6 @@ export declare interface DsInput extends Components.DsInput {
466
506
  }
467
507
 
468
508
 
469
- @ProxyCmp({
470
- inputs: ['size', 'truncate']
471
- })
472
- @Component({
473
- selector: 'ds-label-wrap',
474
- changeDetection: ChangeDetectionStrategy.OnPush,
475
- template: '<ng-content></ng-content>',
476
- // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
477
- inputs: ['size', 'truncate'],
478
- standalone: false
479
- })
480
- export class DsLabelWrap {
481
- protected el: HTMLDsLabelWrapElement;
482
- constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
483
- c.detach();
484
- this.el = r.nativeElement;
485
- }
486
- }
487
-
488
-
489
- export declare interface DsLabelWrap extends Components.DsLabelWrap {}
490
-
491
-
492
509
  @ProxyCmp({
493
510
  inputs: ['label', 'size']
494
511
  })
@@ -925,22 +942,18 @@ export declare interface DsTable extends Components.DsTable {
925
942
 
926
943
 
927
944
  @ProxyCmp({
928
- inputs: ['background', 'contrast', 'elevation', 'inactive', 'intent', 'interactive', 'label', 'maxWidth', 'pressed', 'removable', 'rounded', 'size']
945
+ inputs: ['contrast', 'elevation', 'intent', 'label', 'maxWidth', 'rounded', 'size']
929
946
  })
930
947
  @Component({
931
948
  selector: 'ds-tag',
932
949
  changeDetection: ChangeDetectionStrategy.OnPush,
933
950
  template: '<ng-content></ng-content>',
934
951
  // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
935
- inputs: ['background', 'contrast', 'elevation', 'inactive', 'intent', 'interactive', { name: 'label', required: true }, 'maxWidth', 'pressed', 'removable', 'rounded', 'size'],
936
- outputs: ['dsRemove', 'dsClick', 'dsPressedChange'],
952
+ inputs: ['contrast', 'elevation', 'intent', { name: 'label', required: true }, 'maxWidth', 'rounded', 'size'],
937
953
  standalone: false
938
954
  })
939
955
  export class DsTag {
940
956
  protected el: HTMLDsTagElement;
941
- @Output() dsRemove = new EventEmitter<CustomEvent<void>>();
942
- @Output() dsClick = new EventEmitter<CustomEvent<void>>();
943
- @Output() dsPressedChange = new EventEmitter<CustomEvent<boolean>>();
944
957
  constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
945
958
  c.detach();
946
959
  this.el = r.nativeElement;
@@ -948,31 +961,18 @@ export class DsTag {
948
961
  }
949
962
 
950
963
 
951
- export declare interface DsTag extends Components.DsTag {
952
- /**
953
- * Fired when the remove button is clicked.
954
- */
955
- dsRemove: EventEmitter<CustomEvent<void>>;
956
- /**
957
- * Fired when an interactive tag is clicked.
958
- */
959
- dsClick: EventEmitter<CustomEvent<void>>;
960
- /**
961
- * Fired when the pressed state toggles.
962
- */
963
- dsPressedChange: EventEmitter<CustomEvent<boolean>>;
964
- }
964
+ export declare interface DsTag extends Components.DsTag {}
965
965
 
966
966
 
967
967
  @ProxyCmp({
968
- inputs: ['align', 'as', 'color', 'decoration', 'for', 'italic', 'lineTruncation', 'variant', 'wrap']
968
+ inputs: ['align', 'as', 'color', 'decoration', 'fontFeature', 'for', 'italic', 'lineTruncation', 'variant', 'wrap']
969
969
  })
970
970
  @Component({
971
971
  selector: 'ds-text',
972
972
  changeDetection: ChangeDetectionStrategy.OnPush,
973
973
  template: '<ng-content></ng-content>',
974
974
  // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
975
- inputs: ['align', 'as', 'color', 'decoration', 'for', 'italic', 'lineTruncation', 'variant', 'wrap'],
975
+ inputs: ['align', 'as', 'color', 'decoration', 'fontFeature', 'for', 'italic', 'lineTruncation', 'variant', 'wrap'],
976
976
  standalone: false
977
977
  })
978
978
  export class DsText {
@@ -14,6 +14,7 @@ export { DsButtonGroup } from "./ds-button-group.js";
14
14
  export { DsButton } from "./ds-button.js";
15
15
  export { DsCard } from "./ds-card.js";
16
16
  export { DsCheckbox } from "./ds-checkbox.js";
17
+ export { DsChip } from "./ds-chip.js";
17
18
  export { DsDivider } from "./ds-divider.js";
18
19
  export { DsEmptyState } from "./ds-empty-state.js";
19
20
  export { DsFade } from "./ds-fade.js";
@@ -21,7 +22,6 @@ export { DsField } from "./ds-field.js";
21
22
  export { DsHeader } from "./ds-header.js";
22
23
  export { DsIcon } from "./ds-icon.js";
23
24
  export { DsInput } from "./ds-input.js";
24
- export { DsLabelWrap } from "./ds-label-wrap.js";
25
25
  export { DsLoader } from "./ds-loader.js";
26
26
  export { DsMenu } from "./ds-menu.js";
27
27
  export { DsModal } from "./ds-modal.js";
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * This file was automatically generated by the Stencil React Output Target.
5
+ * Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
6
+ */
7
+
8
+ /* eslint-disable */
9
+
10
+ import type { EventName, StencilReactComponent } from '@stencil/react-output-target/runtime';
11
+ import { createComponent } from '@stencil/react-output-target/runtime';
12
+ import React from 'react';
13
+
14
+ import { type DsChipCustomEvent } from "@ds-mo/ui";
15
+ import type { Components } from "@ds-mo/ui/dist/components";
16
+ import { DsChip as DsChipElement, defineCustomElement as defineDsChip } from "@ds-mo/ui/dist/components/ds-chip.js";
17
+
18
+ export type DsChipEvents = {
19
+ onDsRemove: EventName<DsChipCustomEvent<void>>,
20
+ onDsClick: EventName<DsChipCustomEvent<void>>,
21
+ onDsPressedChange: EventName<DsChipCustomEvent<boolean>>
22
+ };
23
+
24
+ export const DsChip: StencilReactComponent<DsChipElement, DsChipEvents, Components.DsChip> = /*@__PURE__*/ createComponent<DsChipElement, DsChipEvents, Components.DsChip>({
25
+ tagName: 'ds-chip',
26
+ elementClass: DsChipElement,
27
+ // @ts-ignore - ignore potential React type mismatches between the Stencil Output Target and your project.
28
+ react: React,
29
+ events: {
30
+ onDsRemove: 'dsRemove',
31
+ onDsClick: 'dsClick',
32
+ onDsPressedChange: 'dsPressedChange'
33
+ } as DsChipEvents,
34
+ defineCustomElement: defineDsChip
35
+ });
@@ -7,29 +7,20 @@
7
7
 
8
8
  /* eslint-disable */
9
9
 
10
- import type { EventName, StencilReactComponent } from '@stencil/react-output-target/runtime';
10
+ import type { StencilReactComponent } from '@stencil/react-output-target/runtime';
11
11
  import { createComponent } from '@stencil/react-output-target/runtime';
12
12
  import React from 'react';
13
13
 
14
- import { type DsTagCustomEvent } from "@ds-mo/ui";
15
14
  import type { Components } from "@ds-mo/ui/dist/components";
16
15
  import { DsTag as DsTagElement, defineCustomElement as defineDsTag } from "@ds-mo/ui/dist/components/ds-tag.js";
17
16
 
18
- export type DsTagEvents = {
19
- onDsRemove: EventName<DsTagCustomEvent<void>>,
20
- onDsClick: EventName<DsTagCustomEvent<void>>,
21
- onDsPressedChange: EventName<DsTagCustomEvent<boolean>>
22
- };
17
+ export type DsTagEvents = NonNullable<unknown>;
23
18
 
24
19
  export const DsTag: StencilReactComponent<DsTagElement, DsTagEvents, Components.DsTag> = /*@__PURE__*/ createComponent<DsTagElement, DsTagEvents, Components.DsTag>({
25
20
  tagName: 'ds-tag',
26
21
  elementClass: DsTagElement,
27
22
  // @ts-ignore - ignore potential React type mismatches between the Stencil Output Target and your project.
28
23
  react: React,
29
- events: {
30
- onDsRemove: 'dsRemove',
31
- onDsClick: 'dsClick',
32
- onDsPressedChange: 'dsPressedChange'
33
- } as DsTagEvents,
24
+ events: {} as DsTagEvents,
34
25
  defineCustomElement: defineDsTag
35
26
  });
@@ -1,32 +1,82 @@
1
1
  import { Component, Prop, h, Host } from '@stencil/core';
2
2
 
3
+ export type BadgeVariant = 'counter' | 'dot';
4
+ export type BadgeSurface =
5
+ | 'default'
6
+ | 'primary'
7
+ | 'secondary'
8
+ | 'medium'
9
+ | 'bold'
10
+ | 'strong'
11
+ | 'navigation'
12
+ | 'always-dark';
13
+
14
+ const SURFACE_RING: Record<BadgeSurface, string> = {
15
+ default: 'var(--color-background-secondary)',
16
+ primary: 'var(--color-background-primary)',
17
+ secondary: 'var(--color-background-secondary)',
18
+ medium: 'var(--color-background-medium-neutral)',
19
+ bold: 'var(--color-background-bold-neutral)',
20
+ strong: 'var(--color-background-strong-neutral)',
21
+ navigation: 'var(--color-navigation-background)',
22
+ 'always-dark': 'var(--color-always-dark-background)',
23
+ };
24
+
3
25
  @Component({
4
26
  tag: 'ds-badge',
5
27
  styleUrl: 'Badge.css',
6
28
  scoped: true,
7
29
  })
8
30
  export class Badge {
31
+ /** Render as a compact counter or notification dot. */
32
+ @Prop() variant: BadgeVariant = 'counter';
33
+
34
+ /** Count shown for counter badges. Count 0 hides the badge. */
9
35
  @Prop() count: number = 0;
36
+
37
+ /** Highest count shown before compacting to "+". */
38
+ @Prop() max: number = 9;
39
+
40
+ /** Surface context for the ring around dots. */
41
+ @Prop() surface: BadgeSurface = 'default';
42
+
43
+ /** Direct ring background override for component-local surfaces. */
44
+ @Prop() background: string | undefined;
45
+
46
+ /** Deprecated alias for selected counter styling. Prefer context-specific color in the parent. */
10
47
  @Prop() isSelected: boolean = false;
48
+
11
49
  /** Accessible label. Defaults to the count as a string. */
12
50
  @Prop() label: string | undefined;
13
51
 
14
52
  render() {
15
- if (this.count === 0) return <Host style={{ display: 'none' }} />;
53
+ const isDot = this.variant === 'dot';
54
+
55
+ if (!isDot && this.count === 0) return <Host style={{ display: 'none' }} />;
16
56
 
17
- const display = this.count > 9 ? '+' : String(this.count);
18
- const ariaLabel = this.label ?? String(this.count);
57
+ const display = this.count > this.max ? '+' : String(this.count);
58
+ const ariaLabel = this.label ?? (isDot ? 'Notification' : String(this.count));
59
+ const ring = this.background ?? SURFACE_RING[this.surface];
19
60
 
20
61
  return (
21
- <Host class="badge" aria-label={ariaLabel}>
62
+ <Host
63
+ class={{
64
+ badge: true,
65
+ 'badge--counter': !isDot,
66
+ 'badge--dot': isDot,
67
+ 'badge--selected': this.isSelected,
68
+ }}
69
+ aria-label={ariaLabel}
70
+ style={{ '--_badge-ring': ring }}
71
+ >
22
72
  <span
23
73
  class={{
24
- 'text-caption-emphasis': true,
25
- 'badge__text': true,
26
- 'badge__text--selected': this.isSelected,
74
+ 'badge__mark': true,
75
+ 'text-caption-emphasis': !isDot,
27
76
  }}
77
+ aria-hidden="true"
28
78
  >
29
- {display}
79
+ {!isDot && display}
30
80
  </span>
31
81
  </Host>
32
82
  );
@@ -1 +1 @@
1
- export type {} from './Badge';
1
+ export type { BadgeVariant, BadgeSurface } from './Badge';
@@ -87,8 +87,8 @@ export class Banner {
87
87
 
88
88
  private getElevation(): string {
89
89
  return this.floating
90
- ? 'var(--effect-elevation-elevated-floating, var(--effect-elevation-floating))'
91
- : 'var(--effect-elevation-elevated)';
90
+ ? 'var(--effect-elevation-elevated-floating)'
91
+ : 'var(--effect-elevation-elevated-sm)';
92
92
  }
93
93
 
94
94
  render() {
@@ -551,7 +551,13 @@ export class BarNav {
551
551
  {this.activeTabLabel}
552
552
  </span>
553
553
  {this.activeTabHasDot && (
554
- <span class="bar-nav__tab-trigger-dot" aria-hidden="true" />
554
+ <ds-badge
555
+ class="bar-nav__tab-trigger-dot"
556
+ variant="dot"
557
+ background="var(--_dot-ring)"
558
+ label=""
559
+ aria-hidden="true"
560
+ />
555
561
  )}
556
562
  </span>
557
563
  <ds-icon
@@ -58,7 +58,15 @@ export class BarNavAction {
58
58
  >
59
59
  <span class="action-btn__icon-wrap">
60
60
  <ds-icon name={this.icon} size="md" color="inherit" />
61
- {this.dot && <span class="action-btn__dot" aria-hidden="true" />}
61
+ {this.dot && (
62
+ <ds-badge
63
+ class="action-btn__dot"
64
+ variant="dot"
65
+ background="var(--_dot-ring)"
66
+ label=""
67
+ aria-hidden="true"
68
+ />
69
+ )}
62
70
  </span>
63
71
  </button>
64
72
  </Host>
@@ -0,0 +1,127 @@
1
+ import { Component, Prop, Event, EventEmitter, h, Host } from '@stencil/core';
2
+
3
+ export type ChipIntent = 'neutral' | 'brand' | 'ai' | 'negative' | 'warning' | 'caution' | 'positive';
4
+ export type ChipContrast = 'strong' | 'bold' | 'medium' | 'faint';
5
+ export type ChipElevation = 'none' | 'flat' | 'elevated';
6
+ export type ChipSize = 'md' | 'sm' | 'xs';
7
+ export type ChipBackground = 'faint' | 'medium' | 'bold' | 'strong' | 'always-dark';
8
+
9
+ const TEXT_VARIANT: Record<ChipSize, string> = {
10
+ md: 'text-body-medium',
11
+ sm: 'text-body-small',
12
+ xs: 'text-caption',
13
+ };
14
+
15
+ const ICON_SIZE: Record<ChipSize, number> = { md: 20, sm: 16, xs: 12 };
16
+
17
+ @Component({
18
+ tag: 'ds-chip',
19
+ styleUrl: 'Chip.css',
20
+ scoped: true,
21
+ })
22
+ export class Chip {
23
+ @Prop() label!: string;
24
+ @Prop() intent: ChipIntent = 'neutral';
25
+ @Prop() contrast: ChipContrast = 'faint';
26
+ @Prop() elevation: ChipElevation = 'none';
27
+ @Prop() size: ChipSize = 'md';
28
+ @Prop() rounded: boolean = false;
29
+ @Prop() removable: boolean = false;
30
+ @Prop() maxWidth: string | number | undefined;
31
+ @Prop() inactive: boolean = false;
32
+ @Prop() background: ChipBackground | undefined;
33
+ @Prop({ mutable: true }) pressed: boolean = false;
34
+
35
+ /** Fired when the remove button is clicked. */
36
+ @Event() dsRemove!: EventEmitter<void>;
37
+ /** Fired when an interactive chip is clicked. */
38
+ @Event() dsClick!: EventEmitter<void>;
39
+ /** Fired when the pressed state toggles. */
40
+ @Event() dsPressedChange!: EventEmitter<boolean>;
41
+
42
+ private handleClick = () => {
43
+ if (this.inactive) return;
44
+ this.pressed = !this.pressed;
45
+ this.dsPressedChange.emit(this.pressed);
46
+ this.dsClick.emit();
47
+ };
48
+
49
+ private handleKeyDown = (e: KeyboardEvent) => {
50
+ if (this.inactive) return;
51
+ if (e.key === 'Enter' || e.key === ' ') {
52
+ e.preventDefault();
53
+ this.handleClick();
54
+ }
55
+ };
56
+
57
+ private handleRemove = (e: MouseEvent) => {
58
+ e.stopPropagation();
59
+ this.dsRemove.emit();
60
+ };
61
+
62
+ render() {
63
+ const sz = this.size.toUpperCase() as 'MD' | 'SM' | 'XS';
64
+ const textVariant = TEXT_VARIANT[this.size];
65
+ const iconSize = ICON_SIZE[this.size];
66
+
67
+ const maxWidthStyle = this.maxWidth != null
68
+ ? { maxWidth: typeof this.maxWidth === 'number' ? `${this.maxWidth}px` : this.maxWidth }
69
+ : undefined;
70
+
71
+ const hostCls: Record<string, boolean> = {
72
+ 'tag': true,
73
+ [`tag--intent-${this.intent}`]: true,
74
+ [`tag--contrast-${this.contrast}`]: true,
75
+ [`tag--elevation-${this.elevation}`]: true,
76
+ [`tag--size-${this.size}`]: true,
77
+ 'tag--rounded': this.rounded,
78
+ 'tag--rounded-no-icon-left': this.rounded,
79
+ 'tag--rounded-no-remove-right': this.rounded && !this.removable,
80
+ [`tag--icon-left-${sz}`]: false,
81
+ [`tag--icon-right-${sz}`]: this.removable,
82
+ 'tag--removable': this.removable,
83
+ 'tag--interactive': true,
84
+ 'tag--inactive': this.inactive,
85
+ 'tag--pressed': this.pressed,
86
+ 'tag--on-medium': this.background === 'medium',
87
+ 'tag--on-bold': this.background === 'bold',
88
+ 'tag--on-strong': this.background === 'strong',
89
+ 'tag--on-always-dark': this.background === 'always-dark',
90
+ };
91
+
92
+ return (
93
+ <Host
94
+ class={hostCls}
95
+ style={maxWidthStyle}
96
+ onClick={this.handleClick}
97
+ onKeyDown={this.handleKeyDown}
98
+ role="button"
99
+ tabIndex={this.inactive ? -1 : 0}
100
+ aria-pressed={String(this.pressed)}
101
+ aria-disabled={this.inactive || undefined}
102
+ >
103
+ <span class="tag__icon-slot" style={{ fontSize: `${iconSize}px`, lineHeight: '0' }}>
104
+ <slot name="icon" />
105
+ </span>
106
+ <span class={{ 'tag__label': true, [textVariant]: true }}>
107
+ {this.label}
108
+ </span>
109
+ {this.removable && (
110
+ <button
111
+ type="button"
112
+ class="tag__remove"
113
+ onClick={this.handleRemove}
114
+ aria-label={`Remove ${this.label}`}
115
+ tabIndex={-1}
116
+ >
117
+ <slot name="remove-icon">
118
+ <span class="tag__remove-x" style={{ fontSize: `${iconSize}px`, lineHeight: '0' }}>
119
+
120
+ </span>
121
+ </slot>
122
+ </button>
123
+ )}
124
+ </Host>
125
+ );
126
+ }
127
+ }
@@ -0,0 +1 @@
1
+ export type { ChipIntent, ChipContrast, ChipElevation, ChipSize, ChipBackground } from './Chip';