@synergy-design-system/mcp 2.6.1 → 2.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.
Files changed (78) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/utilities/storybook/scraper.js +14 -3
  3. package/metadata/checksum.txt +1 -1
  4. package/metadata/packages/components/components/syn-alert/component.styles.ts +126 -26
  5. package/metadata/packages/components/components/syn-alert/component.ts +1 -2
  6. package/metadata/packages/components/components/syn-checkbox/component.angular.ts +13 -0
  7. package/metadata/packages/components/components/syn-checkbox/component.styles.ts +99 -39
  8. package/metadata/packages/components/components/syn-checkbox/component.ts +13 -10
  9. package/metadata/packages/components/components/syn-checkbox/component.vue +5 -0
  10. package/metadata/packages/components/components/syn-combobox/component.angular.ts +13 -0
  11. package/metadata/packages/components/components/syn-combobox/component.styles.ts +216 -193
  12. package/metadata/packages/components/components/syn-combobox/component.ts +68 -39
  13. package/metadata/packages/components/components/syn-combobox/component.vue +5 -0
  14. package/metadata/packages/components/components/syn-file/component.angular.ts +13 -0
  15. package/metadata/packages/components/components/syn-file/component.styles.ts +20 -3
  16. package/metadata/packages/components/components/syn-file/component.ts +19 -5
  17. package/metadata/packages/components/components/syn-file/component.vue +5 -0
  18. package/metadata/packages/components/components/syn-input/component.ts +1 -2
  19. package/metadata/packages/components/components/syn-popup/component.styles.ts +24 -17
  20. package/metadata/packages/components/components/syn-popup/component.ts +1 -2
  21. package/metadata/packages/components/components/syn-radio/component.angular.ts +13 -0
  22. package/metadata/packages/components/components/syn-radio/component.styles.ts +91 -29
  23. package/metadata/packages/components/components/syn-radio/component.ts +19 -10
  24. package/metadata/packages/components/components/syn-radio/component.vue +5 -0
  25. package/metadata/packages/components/components/syn-radio-group/component.styles.ts +30 -9
  26. package/metadata/packages/components/components/syn-radio-group/component.ts +61 -32
  27. package/metadata/packages/components/components/syn-range/component.angular.ts +13 -0
  28. package/metadata/packages/components/components/syn-range/component.styles.ts +27 -3
  29. package/metadata/packages/components/components/syn-range/component.ts +17 -5
  30. package/metadata/packages/components/components/syn-range/component.vue +5 -0
  31. package/metadata/packages/components/components/syn-select/component.angular.ts +13 -0
  32. package/metadata/packages/components/components/syn-select/component.styles.ts +222 -151
  33. package/metadata/packages/components/components/syn-select/component.ts +30 -15
  34. package/metadata/packages/components/components/syn-select/component.vue +5 -0
  35. package/metadata/packages/components/components/syn-switch/component.angular.ts +13 -0
  36. package/metadata/packages/components/components/syn-switch/component.styles.ts +145 -63
  37. package/metadata/packages/components/components/syn-switch/component.ts +16 -4
  38. package/metadata/packages/components/components/syn-switch/component.vue +5 -0
  39. package/metadata/packages/components/components/syn-textarea/component.styles.ts +55 -27
  40. package/metadata/packages/components/components/syn-textarea/component.ts +1 -3
  41. package/metadata/packages/components/components/syn-tooltip/component.styles.ts +15 -10
  42. package/metadata/packages/components/components/syn-tooltip/component.ts +13 -5
  43. package/metadata/packages/components/components/syn-validate/component.angular.ts +9 -0
  44. package/metadata/packages/components/components/syn-validate/component.react.ts +8 -0
  45. package/metadata/packages/components/components/syn-validate/component.ts +106 -8
  46. package/metadata/packages/components/components/syn-validate/component.vue +9 -0
  47. package/metadata/packages/components/static/CHANGELOG.md +43 -0
  48. package/metadata/packages/tokens/CHANGELOG.md +33 -0
  49. package/metadata/packages/tokens/dark.css +7 -1
  50. package/metadata/packages/tokens/index.js +31 -1
  51. package/metadata/packages/tokens/light.css +7 -1
  52. package/metadata/packages/tokens/sick2018_dark.css +7 -1
  53. package/metadata/packages/tokens/sick2018_light.css +7 -1
  54. package/metadata/packages/tokens/sick2025_dark.css +7 -1
  55. package/metadata/packages/tokens/sick2025_light.css +7 -1
  56. package/metadata/static/components/syn-checkbox/docs.md +36 -0
  57. package/metadata/static/components/syn-combobox/docs.md +138 -0
  58. package/metadata/static/components/syn-file/docs.md +24 -0
  59. package/metadata/static/components/syn-input/docs.md +1 -1
  60. package/metadata/static/components/syn-radio/docs.md +21 -0
  61. package/metadata/static/components/syn-radio-group/docs.md +46 -0
  62. package/metadata/static/components/syn-range/docs.md +19 -0
  63. package/metadata/static/components/syn-select/docs.md +81 -0
  64. package/metadata/static/components/syn-switch/docs.md +22 -0
  65. package/metadata/static/components/syn-textarea/docs.md +1 -1
  66. package/metadata/static/components/syn-tooltip/docs.md +73 -0
  67. package/metadata/static/components/syn-validate/docs.md +33 -6
  68. package/package.json +4 -4
  69. package/metadata/packages/components/components/syn-alert/component.custom.styles.ts +0 -136
  70. package/metadata/packages/components/components/syn-checkbox/component.custom.styles.ts +0 -86
  71. package/metadata/packages/components/components/syn-combobox/component.custom.styles.ts +0 -122
  72. package/metadata/packages/components/components/syn-popup/component.custom.styles.ts +0 -18
  73. package/metadata/packages/components/components/syn-radio/component.custom.styles.ts +0 -86
  74. package/metadata/packages/components/components/syn-radio-group/component.custom.styles.ts +0 -25
  75. package/metadata/packages/components/components/syn-select/component.custom.styles.ts +0 -175
  76. package/metadata/packages/components/components/syn-switch/component.custom.styles.ts +0 -141
  77. package/metadata/packages/components/components/syn-textarea/component.custom.styles.ts +0 -48
  78. package/metadata/packages/components/components/syn-tooltip/component.custom.styles.ts +0 -13
@@ -13,13 +13,11 @@ import { waitForEvent } from '../../internal/event.js';
13
13
  import { watch } from '../../internal/watch.js';
14
14
  import componentStyles from '../../styles/component.styles.js';
15
15
  import formControlStyles from '../../styles/form-control.styles.js';
16
- import formControlCustomStyles from '../../styles/form-control.custom.styles.js';
17
16
  import SynergyElement from '../../internal/synergy-element.js';
18
17
  import SynIcon from '../icon/icon.component.js';
19
18
  import SynPopup from '../popup/popup.component.js';
20
19
  import SynTag from '../tag/tag.component.js';
21
20
  import styles from './select.styles.js';
22
- import customStyles from './select.custom.styles.js';
23
21
  import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
24
22
  import type { SynergyFormControl } from '../../internal/synergy-element.js';
25
23
  import type { SynRemoveEvent } from '../../events/syn-remove.js';
@@ -77,7 +75,7 @@ import { enableDefaultSettings } from '../../utilities/defaultSettings/decorator
77
75
  */
78
76
  @enableDefaultSettings('SynSelect')
79
77
  export default class SynSelect extends SynergyElement implements SynergyFormControl {
80
- static styles: CSSResultGroup = [componentStyles, formControlStyles, styles, formControlCustomStyles, customStyles];
78
+ static styles: CSSResultGroup = [componentStyles, formControlStyles, styles];
81
79
  static dependencies = {
82
80
  'syn-icon': SynIcon,
83
81
  'syn-popup': SynPopup,
@@ -168,6 +166,9 @@ export default class SynSelect extends SynergyElement implements SynergyFormCont
168
166
  /** Disables the select control. */
169
167
  @property({ type: Boolean, reflect: true }) disabled = false;
170
168
 
169
+ /** Sets the select to a readonly state. */
170
+ @property({ reflect: true, type: Boolean }) readonly = false;
171
+
171
172
  /** Adds a clear button when the select is not empty. */
172
173
  @property({ type: Boolean }) clearable = false;
173
174
 
@@ -458,6 +459,12 @@ export default class SynSelect extends SynergyElement implements SynergyFormCont
458
459
  }
459
460
  };
460
461
 
462
+ private handleFormControlClick() {
463
+ if (this.readonly) {
464
+ this.displayInput.focus();
465
+ }
466
+ }
467
+
461
468
  private handleLabelClick() {
462
469
  this.displayInput.focus();
463
470
  }
@@ -467,7 +474,7 @@ export default class SynSelect extends SynergyElement implements SynergyFormCont
467
474
  const isIconButton = path.some(el => el instanceof Element && el.tagName.toLowerCase() === 'syn-icon-button');
468
475
 
469
476
  // Ignore disabled controls and clicks on tags (remove buttons)
470
- if (this.disabled || isIconButton) {
477
+ if (this.disabled || this.readonly || isIconButton) {
471
478
  return;
472
479
  }
473
480
 
@@ -571,7 +578,7 @@ export default class SynSelect extends SynergyElement implements SynergyFormCont
571
578
 
572
579
  this.valueHasChanged = true;
573
580
 
574
- if (!this.disabled) {
581
+ if (!this.disabled && !this.readonly) {
575
582
  this.toggleOptionSelection(option, false);
576
583
 
577
584
  // Emit after updating
@@ -654,7 +661,12 @@ export default class SynSelect extends SynergyElement implements SynergyFormCont
654
661
  if (this.multiple) {
655
662
  this.value = this.selectedOptions.map(el => el.value);
656
663
 
657
- if (this.placeholder && this.value.length === 0) {
664
+ // #1177: When using a readonly field with multiple set,
665
+ // set the display label to the list of selected options instead of the count, since the user can't open the listbox to see which options are selected.
666
+ // This makes it possible to copy the values from the readonly select.
667
+ if (this.readonly) {
668
+ this.displayLabel = this.selectedOptions.map(opt => opt.getTextLabel()).join(', ');
669
+ } else if (this.placeholder && this.value.length === 0) {
658
670
  // When no items are selected, keep the value empty so the placeholder shows
659
671
  this.displayLabel = '';
660
672
  } else {
@@ -702,10 +714,10 @@ export default class SynSelect extends SynergyElement implements SynergyFormCont
702
714
  });
703
715
  }
704
716
 
705
- @watch('disabled', { waitUntilFirstUpdate: true })
717
+ @watch(['disabled', 'readonly'], { waitUntilFirstUpdate: true })
706
718
  handleDisabledChange() {
707
- // Close the listbox when the control is disabled
708
- if (this.disabled) {
719
+ // Close the listbox when the control is disabled or readonly
720
+ if (this.disabled || this.readonly) {
709
721
  this.open = false;
710
722
  this.handleOpenChange();
711
723
  }
@@ -774,7 +786,7 @@ protected override willUpdate(changedProperties: PropertyValues) {
774
786
 
775
787
  @watch('open', { waitUntilFirstUpdate: true })
776
788
  async handleOpenChange() {
777
- if (this.open && !this.disabled) {
789
+ if (this.open && (!this.disabled && !this.readonly)) {
778
790
  // Reset the current option
779
791
  this.setCurrentOption(this.selectedOptions[0] || this.getFirstOption());
780
792
 
@@ -817,7 +829,7 @@ protected override willUpdate(changedProperties: PropertyValues) {
817
829
 
818
830
  /** Shows the listbox. */
819
831
  async show() {
820
- if (this.open || this.disabled) {
832
+ if (this.open || this.disabled || this.readonly) {
821
833
  this.open = false;
822
834
  return undefined;
823
835
  }
@@ -828,7 +840,7 @@ protected override willUpdate(changedProperties: PropertyValues) {
828
840
 
829
841
  /** Hides the listbox. */
830
842
  async hide() {
831
- if (!this.open || this.disabled) {
843
+ if (!this.open || this.disabled || this.readonly) {
832
844
  this.open = false;
833
845
  return undefined;
834
846
  }
@@ -874,7 +886,7 @@ protected override willUpdate(changedProperties: PropertyValues) {
874
886
  const hasHelpTextSlot = this.hasSlotController.test('help-text');
875
887
  const hasLabel = this.label ? true : !!hasLabelSlot;
876
888
  const hasHelpText = this.helpText ? true : !!hasHelpTextSlot;
877
- const hasClearIcon = this.clearable && !this.disabled && hasValue;
889
+ const hasClearIcon = this.clearable && (!this.disabled && !this.readonly) && hasValue;
878
890
  const isPlaceholderVisible = this.placeholder && this.value && !hasValue;
879
891
 
880
892
  return html`
@@ -886,8 +898,9 @@ protected override willUpdate(changedProperties: PropertyValues) {
886
898
  'form-control--medium': this.size === 'medium',
887
899
  'form-control--large': this.size === 'large',
888
900
  'form-control--has-label': hasLabel,
889
- 'form-control--has-help-text': hasHelpText
901
+ 'form-control--has-help-text': hasHelpText,
890
902
  })}
903
+ @click=${this.handleFormControlClick}
891
904
  >
892
905
  <label
893
906
  id="label"
@@ -906,6 +919,7 @@ protected override willUpdate(changedProperties: PropertyValues) {
906
919
  'select--standard': true,
907
920
  'select--open': this.open,
908
921
  'select--disabled': this.disabled,
922
+ 'select--readonly': this.readonly,
909
923
  'select--multiple': this.multiple,
910
924
  'select--focused': this.hasFocus,
911
925
  'select--placeholder-visible': isPlaceholderVisible,
@@ -955,12 +969,13 @@ protected override willUpdate(changedProperties: PropertyValues) {
955
969
  @blur=${this.handleBlur}
956
970
  />
957
971
 
958
- ${this.multiple ? html`<div part="tags" class="select__tags">${this.tags}</div>` : ''}
972
+ ${this.multiple && !this.readonly ? html`<div part="tags" class="select__tags">${this.tags}</div>` : ''}
959
973
 
960
974
  <input
961
975
  class="select__value-input"
962
976
  type="text"
963
977
  ?disabled=${this.disabled}
978
+ ?readonly=${this.readonly}
964
979
  ?required=${this.required}
965
980
  .value=${Array.isArray(this.value) ? this.value.join(', ') : this.value?.toString()}
966
981
  tabindex="-1"
@@ -116,6 +116,11 @@ indicate the number of additional items that are selected.
116
116
  */
117
117
  disabled?: SynSelect['disabled'];
118
118
 
119
+ /**
120
+ * Sets the select to a readonly state.
121
+ */
122
+ readonly?: SynSelect['readonly'];
123
+
119
124
  /**
120
125
  * Adds a clear button when the select is not empty.
121
126
  */
@@ -153,6 +153,19 @@ export class SynSwitchComponent {
153
153
  return this.nativeElement.disabled;
154
154
  }
155
155
 
156
+ /**
157
+ * Sets the switch to a readonly state.
158
+ */
159
+ @Input()
160
+ set readonly(v: '' | SynSwitch['readonly']) {
161
+ this._ngZone.runOutsideAngular(
162
+ () => (this.nativeElement.readonly = v === '' || v),
163
+ );
164
+ }
165
+ get readonly(): SynSwitch['readonly'] {
166
+ return this.nativeElement.readonly;
167
+ }
168
+
156
169
  /**
157
170
  * Draws the switch in a checked state.
158
171
  */
@@ -2,165 +2,247 @@
2
2
  import { css } from 'lit';
3
3
 
4
4
  export default css`
5
- /* stylelint-disable */
5
+ /* stylelint-disable no-descending-specificity */
6
6
  :host {
7
7
  display: inline-block;
8
8
  }
9
9
 
10
10
  :host([size='small']) {
11
- --height: var(--syn-toggle-size-small);
12
- --thumb-size: calc(var(--syn-toggle-size-small) + 4px);
13
- --width: calc(var(--height) * 2);
11
+ --height: var(--syn-switch-height-small);
12
+ --thumb-size: var(--syn-toggle-size-small);
13
+ --width: var(--syn-switch-width-small);
14
14
 
15
15
  font-size: var(--syn-input-font-size-small);
16
16
  }
17
17
 
18
18
  :host([size='medium']) {
19
- --height: var(--syn-toggle-size-medium);
20
- --thumb-size: calc(var(--syn-toggle-size-medium) + 4px);
21
- --width: calc(var(--height) * 2);
19
+ --height: var(--syn-switch-height-medium);
20
+ --thumb-size: var(--syn-toggle-size-medium);
21
+ --width: var(--syn-switch-width-medium);
22
22
 
23
23
  font-size: var(--syn-input-font-size-medium);
24
24
  }
25
25
 
26
26
  :host([size='large']) {
27
- --height: var(--syn-toggle-size-large);
28
- --thumb-size: calc(var(--syn-toggle-size-large) + 4px);
29
- --width: calc(var(--height) * 2);
27
+ --height: var(--syn-switch-height-large);
28
+ --thumb-size: var(--syn-toggle-size-large);
29
+ --width: var(--syn-switch-width-large);
30
30
 
31
31
  font-size: var(--syn-input-font-size-large);
32
32
  }
33
33
 
34
34
  .switch {
35
- position: relative;
36
- display: inline-flex;
37
35
  align-items: center;
36
+ color: var(--syn-input-label-color);
37
+ cursor: pointer;
38
+ display: inline-flex;
38
39
  font-family: var(--syn-input-font-family);
39
40
  font-size: inherit;
40
41
  font-weight: var(--syn-input-font-weight);
41
- color: var(--syn-input-label-color);
42
+ position: relative;
42
43
  vertical-align: middle;
43
- cursor: pointer;
44
+ }
45
+
46
+ .switch.switch--small {
47
+ padding: var(--syn-spacing-2x-small) 0;
48
+ }
49
+
50
+ .switch.switch--medium {
51
+ padding: var(--syn-spacing-2x-small) 0;
52
+ }
53
+
54
+ .switch.switch--large {
55
+ padding: var(--syn-spacing-3x-small) 0;
56
+ }
57
+
58
+ /* Hint: can be removed, if the padding stylings for sizes from above are removed */
59
+ .form-control--has-help-text .switch {
60
+ padding-bottom: 0;
44
61
  }
45
62
 
46
63
  .switch__control {
47
- flex: 0 0 auto;
48
- position: relative;
49
- display: inline-flex;
50
64
  align-items: center;
51
- justify-content: center;
52
- width: var(--width);
53
- height: var(--height);
54
- background-color: var(--syn-color-neutral-400);
55
- border: solid var(--syn-input-border-width) var(--syn-color-neutral-400);
65
+ background-color: var(--syn-input-icon-icon-clearable-color);
66
+ border: solid var(--syn-border-width-medium) var(--syn-input-icon-icon-clearable-color);
56
67
  border-radius: var(--height);
68
+ display: inline-flex;
69
+ flex: 0 0 auto;
70
+ height: var(--height);
71
+ justify-content: center;
72
+ position: relative;
57
73
  transition:
58
74
  var(--syn-transition-fast) border-color,
59
75
  var(--syn-transition-fast) background-color;
76
+ width: var(--width);
60
77
  }
61
78
 
62
79
  .switch__control .switch__thumb {
63
- width: var(--thumb-size);
64
- height: var(--thumb-size);
65
80
  background-color: var(--syn-color-neutral-0);
81
+ border: none;
66
82
  border-radius: 50%;
67
- border: solid var(--syn-input-border-width) var(--syn-color-neutral-400);
68
- translate: calc((var(--width) - var(--height)) / -2);
83
+ height: var(--thumb-size);
69
84
  transition:
70
85
  var(--syn-transition-fast) translate ease,
71
86
  var(--syn-transition-fast) background-color,
72
87
  var(--syn-transition-fast) border-color,
73
88
  var(--syn-transition-fast) box-shadow;
89
+ translate: calc((var(--width) - var(--height)) / -2);
90
+ width: var(--thumb-size);
74
91
  }
75
92
 
76
93
  .switch__input {
77
- position: absolute;
94
+ margin: 0;
78
95
  opacity: 0;
79
96
  padding: 0;
80
- margin: 0;
81
97
  pointer-events: none;
98
+ position: absolute;
82
99
  }
83
100
 
84
101
  /* Hover */
85
- .switch:not(.switch--checked):not(.switch--disabled) .switch__control:hover {
86
- background-color: var(--syn-color-neutral-400);
87
- border-color: var(--syn-color-neutral-400);
88
- }
89
-
90
- .switch:not(.switch--checked):not(.switch--disabled) .switch__control:hover .switch__thumb {
91
- background-color: var(--syn-color-neutral-0);
92
- border-color: var(--syn-color-neutral-400);
102
+ .switch:not(.switch--checked):not(.switch--disabled):not(.switch--readonly):hover .switch__control {
103
+ background-color: var(--syn-input-icon-icon-clearable-color-hover);
104
+ border-color: var(--syn-input-icon-icon-clearable-color-hover);
93
105
  }
94
106
 
95
107
  /* Focus */
96
- .switch:not(.switch--checked):not(.switch--disabled) .switch__input:focus-visible ~ .switch__control {
97
- background-color: var(--syn-color-neutral-400);
98
- border-color: var(--syn-color-neutral-400);
108
+ .switch:not(.switch--checked):not(.switch--disabled):not(.switch--readonly) .switch__input:focus-visible ~ .switch__control {
109
+ background-color: var(--syn-input-icon-icon-clearable-color);
110
+ border-color: var(--syn-input-icon-icon-clearable-color);
111
+ outline: var(--syn-focus-ring);
112
+ outline-offset: var(--syn-focus-ring-offset);
99
113
  }
100
114
 
101
- .switch:not(.switch--checked):not(.switch--disabled) .switch__input:focus-visible ~ .switch__control .switch__thumb {
115
+ .switch:not(.switch--checked):not(.switch--disabled):not(.switch--readonly) .switch__input:focus-visible ~ .switch__control .switch__thumb {
102
116
  background-color: var(--syn-color-neutral-0);
103
- border-color: var(--syn-color-primary-600);
104
- outline: var(--syn-focus-ring);
105
- outline-offset: var(--syn-focus-ring-offset);
117
+ outline: none;
106
118
  }
107
119
 
108
120
  /* Checked */
109
121
  .switch--checked .switch__control {
110
- background-color: var(--syn-color-primary-600);
111
- border-color: var(--syn-color-primary-600);
122
+ background-color: var(--syn-interactive-emphasis-color);
123
+ border-color: var(--syn-interactive-emphasis-color);
112
124
  }
113
125
 
114
126
  .switch--checked .switch__control .switch__thumb {
115
127
  background-color: var(--syn-color-neutral-0);
116
- border-color: var(--syn-color-primary-600);
117
128
  translate: calc((var(--width) - var(--height)) / 2);
118
129
  }
119
130
 
120
131
  /* Checked + hover */
121
- .switch.switch--checked:not(.switch--disabled) .switch__control:hover {
122
- background-color: var(--syn-color-primary-600);
123
- border-color: var(--syn-color-primary-600);
132
+ .switch.switch--checked:not(.switch--disabled):not(.switch--readonly):hover .switch__control {
133
+ background-color: var(--syn-interactive-emphasis-color-hover);
134
+ border-color: var(--syn-interactive-emphasis-color-hover);
124
135
  }
125
136
 
126
- .switch.switch--checked:not(.switch--disabled) .switch__control:hover .switch__thumb {
137
+ .switch.switch--checked:not(.switch--disabled):not(.switch--readonly):hover .switch__control .switch__thumb {
127
138
  background-color: var(--syn-color-neutral-0);
128
- border-color: var(--syn-color-primary-600);
129
139
  }
130
140
 
131
141
  /* Checked + focus */
132
- .switch.switch--checked:not(.switch--disabled) .switch__input:focus-visible ~ .switch__control {
133
- background-color: var(--syn-color-primary-600);
134
- border-color: var(--syn-color-primary-600);
142
+ .switch.switch--checked:not(.switch--disabled):not(.switch--readonly) .switch__input:focus-visible ~ .switch__control {
143
+ background-color: var(--syn-interactive-emphasis-color);
144
+ border-color: var(--syn-interactive-emphasis-color);
145
+ outline: var(--syn-focus-ring);
146
+ outline-offset: var(--syn-focus-ring-offset);
135
147
  }
136
148
 
137
- .switch.switch--checked:not(.switch--disabled) .switch__input:focus-visible ~ .switch__control .switch__thumb {
149
+ .switch.switch--checked:not(.switch--disabled):not(.switch--readonly) .switch__input:focus-visible ~ .switch__control .switch__thumb {
138
150
  background-color: var(--syn-color-neutral-0);
139
- border-color: var(--syn-color-primary-600);
140
- outline: var(--syn-focus-ring);
141
- outline-offset: var(--syn-focus-ring-offset);
151
+ outline: none;
152
+ }
153
+
154
+ /*
155
+ * #443: Add active styles
156
+ * The checked and unchecked states have different active colors
157
+ * Note the fallback is defined to match the hover color.
158
+ * This is done to make sure no active state is shown at all if no active color is defined.
159
+ * Still better than showing one for the unchecked state but not for the checked state.
160
+ */
161
+ .switch:not(.switch--checked):not(.switch--disabled):not(.switch--readonly):active .switch__control {
162
+ background: var(--syn-input-icon-icon-clearable-color-active);
163
+ border-color: var(--syn-input-icon-icon-clearable-color-active);
164
+ }
165
+
166
+ .switch.switch--checked:not(.switch--disabled):not(.switch--readonly):active .switch__control {
167
+ background: var(--syn-interactive-emphasis-color-active);
168
+ border-color: var(--syn-interactive-emphasis-color-active);
142
169
  }
143
170
 
144
- /* Disabled */
171
+ /** #429: Use token for opacity */
145
172
  .switch--disabled {
146
- opacity: 0.5;
147
173
  cursor: not-allowed;
174
+ opacity: var(--syn-input-disabled-opacity);
148
175
  }
149
176
 
150
177
  .switch__label {
151
178
  display: inline-block;
152
179
  line-height: var(--height);
153
- margin-inline-start: 0.5em;
180
+ margin-inline-start: var(--syn-spacing-x-small);
154
181
  user-select: none;
155
- -webkit-user-select: none;
156
182
  }
157
183
 
158
184
  :host([required]) .switch__label::after {
159
- content: var(--syn-input-required-content);
160
185
  color: var(--syn-input-required-content-color);
186
+ content: var(--syn-input-required-content);
161
187
  margin-inline-start: var(--syn-input-required-content-offset);
162
188
  }
163
189
 
190
+ :host([data-user-invalid]) .switch:not(.switch--checked):not(.switch--disabled):not(.switch--readonly) .switch__control {
191
+ background-color: var(--syn-input-border-color-focus-error);
192
+ border-color: var(--syn-input-border-color-focus-error);
193
+ }
194
+
195
+ :host([data-user-invalid]) .switch:not(.switch--checked):not(.switch--disabled):not(.switch--readonly):hover .switch__control {
196
+ background-color: var(--syn-input-border-color-hover);
197
+ border-color: var(--syn-input-border-color-hover);
198
+ }
199
+
200
+ /**
201
+ * #1178: Readonly state
202
+ */
203
+ .switch.switch--readonly {
204
+ cursor: default;
205
+ }
206
+
207
+ .switch.switch--readonly .switch__label {
208
+ user-select: auto;
209
+ }
210
+
211
+ .switch.switch--readonly .switch__control {
212
+ background: var(--syn-readonly-background-color);
213
+ border-color: var(--syn-readonly-background-color);
214
+ cursor: default;
215
+ }
216
+
217
+ .switch.switch--readonly:hover .switch__control {
218
+ background: var(--syn-input-readonly-background-color-hover);
219
+ border-color: var(--syn-input-readonly-background-color-hover);
220
+ }
221
+
222
+ .switch.switch--readonly .switch__input:focus ~ .switch__control {
223
+ outline: var(--syn-focus-ring);
224
+ outline-offset: var(--syn-focus-ring-offset);
225
+ }
226
+
227
+ .switch.switch--readonly .switch__control .switch__thumb {
228
+ background: var(--syn-readonly-indicator-color);
229
+ }
230
+
231
+ /* Override base styles to remove outline from thumb for readonly switches */
232
+ .switch.switch--readonly .switch__input:focus-visible ~ .switch__control .switch__thumb {
233
+ outline: none;
234
+ }
235
+
236
+ .switch.switch--readonly.switch--checked .switch__input:focus-visible ~ .switch__control .switch__thumb {
237
+ outline: none !important;
238
+ }
239
+
240
+ /* Focus override */
241
+ .switch.switch--readonly .switch__input:focus-visible ~ .switch__control {
242
+ background-color: var(--syn-readonly-background-color);
243
+ border-color: var(--syn-readonly-background-color);
244
+ }
245
+
164
246
  @media (forced-colors: active) {
165
247
  .switch.switch--checked:not(.switch--disabled) .switch__control:hover .switch__thumb,
166
248
  .switch--checked .switch__control .switch__thumb {
@@ -10,10 +10,8 @@ import { property, query, state } from 'lit/decorators.js';
10
10
  import { watch } from '../../internal/watch.js';
11
11
  import componentStyles from '../../styles/component.styles.js';
12
12
  import formControlStyles from '../../styles/form-control.styles.js';
13
- import formControlCustomStyles from '../../styles/form-control.custom.styles.js';
14
13
  import SynergyElement from '../../internal/synergy-element.js';
15
14
  import styles from './switch.styles.js';
16
- import customStyles from './switch.custom.styles.js';
17
15
  import type { CSSResultGroup } from 'lit';
18
16
  import type { SynergyFormControl } from '../../internal/synergy-element.js';
19
17
  import { enableDefaultSettings } from '../../utilities/defaultSettings/decorator.js';
@@ -45,7 +43,7 @@ import { enableDefaultSettings } from '../../utilities/defaultSettings/decorator
45
43
  */
46
44
  @enableDefaultSettings('SynSwitch')
47
45
  export default class SynSwitch extends SynergyElement implements SynergyFormControl {
48
- static styles: CSSResultGroup = [componentStyles, formControlStyles, styles, formControlCustomStyles, customStyles];
46
+ static styles: CSSResultGroup = [componentStyles, formControlStyles, styles];
49
47
 
50
48
  private readonly formControlController = new FormControlController(this, {
51
49
  value: (control: SynSwitch) => (control.checked ? control.value || 'on' : undefined),
@@ -71,6 +69,9 @@ export default class SynSwitch extends SynergyElement implements SynergyFormCont
71
69
  /** Disables the switch. */
72
70
  @property({ type: Boolean, reflect: true }) disabled = false;
73
71
 
72
+ /** Sets the switch to a readonly state. */
73
+ @property({ type: Boolean, reflect: true }) readonly = false;
74
+
74
75
  /** Draws the switch in a checked state. */
75
76
  @property({ type: Boolean, reflect: true }) checked = false;
76
77
 
@@ -118,7 +119,12 @@ export default class SynSwitch extends SynergyElement implements SynergyFormCont
118
119
  this.formControlController.emitInvalidEvent(event);
119
120
  }
120
121
 
121
- private handleClick() {
122
+ private handleClick(e: MouseEvent) {
123
+ if (this.readonly) {
124
+ e.preventDefault();
125
+ return;
126
+ }
127
+
122
128
  this.checked = !this.checked;
123
129
  this.emit('syn-change');
124
130
  }
@@ -129,6 +135,10 @@ export default class SynSwitch extends SynergyElement implements SynergyFormCont
129
135
  }
130
136
 
131
137
  private handleKeyDown(event: KeyboardEvent) {
138
+ if (this.readonly) {
139
+ return;
140
+ }
141
+
132
142
  if (event.key === 'ArrowLeft') {
133
143
  event.preventDefault();
134
144
  this.checked = false;
@@ -212,6 +222,7 @@ export default class SynSwitch extends SynergyElement implements SynergyFormCont
212
222
  switch: true,
213
223
  'switch--checked': this.checked,
214
224
  'switch--disabled': this.disabled,
225
+ 'switch--readonly': this.readonly,
215
226
  'switch--focused': this.hasFocus,
216
227
  'switch--small': this.size === 'small',
217
228
  'switch--medium': this.size === 'medium',
@@ -226,6 +237,7 @@ export default class SynSwitch extends SynergyElement implements SynergyFormCont
226
237
  value=${ifDefined(this.value)}
227
238
  .checked=${live(this.checked)}
228
239
  .disabled=${this.disabled}
240
+ .readOnly=${this.readonly}
229
241
  .required=${this.required}
230
242
  role="switch"
231
243
  aria-checked=${this.checked ? 'true' : 'false'}
@@ -71,6 +71,11 @@ const props = defineProps<{
71
71
  */
72
72
  disabled?: SynSwitch['disabled'];
73
73
 
74
+ /**
75
+ * Sets the switch to a readonly state.
76
+ */
77
+ readonly?: SynSwitch['readonly'];
78
+
74
79
  /**
75
80
  * Draws the switch in a checked state.
76
81
  */