@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
@@ -1,28 +1,29 @@
1
- /* eslint-disable */
2
1
  import { css } from 'lit';
3
2
 
4
3
  export default css`
5
- /* stylelint-disable */
4
+ /* stylelint-disable no-descending-specificity */
5
+ /* stylelint-disable plugin/no-unsupported-browser-features */
6
+ /* stylelint-disable property-no-vendor-prefix */
6
7
  :host {
7
8
  display: block;
8
9
  }
9
10
 
10
11
  .textarea {
11
- display: grid;
12
12
  align-items: center;
13
- position: relative;
14
- width: 100%;
13
+ cursor: text;
14
+ display: grid;
15
15
  font-family: var(--syn-input-font-family);
16
16
  font-weight: var(--syn-input-font-weight);
17
- line-height: var(--syn-line-height-normal);
18
17
  letter-spacing: var(--syn-input-letter-spacing);
19
- vertical-align: middle;
18
+ line-height: var(--syn-line-height-normal);
19
+ position: relative;
20
20
  transition:
21
21
  var(--syn-transition-fast) color,
22
22
  var(--syn-transition-fast) border,
23
23
  var(--syn-transition-fast) box-shadow,
24
24
  var(--syn-transition-fast) background-color;
25
- cursor: text;
25
+ vertical-align: middle;
26
+ width: 100%;
26
27
  }
27
28
 
28
29
  /* Standard textareas */
@@ -35,6 +36,7 @@ export default css`
35
36
  background-color: var(--syn-input-background-color-hover);
36
37
  border-color: var(--syn-input-border-color-hover);
37
38
  }
39
+
38
40
  .textarea--standard:hover:not(.textarea--disabled) .textarea__control {
39
41
  color: var(--syn-input-color-hover);
40
42
  }
@@ -42,8 +44,8 @@ export default css`
42
44
  .textarea--standard.textarea--focused:not(.textarea--disabled) {
43
45
  background-color: var(--syn-input-background-color-focus);
44
46
  border-color: var(--syn-input-border-color-focus);
45
- color: var(--syn-input-color-focus);
46
47
  box-shadow: 0 0 0 var(--syn-focus-ring-width) var(--syn-input-focus-ring-color);
48
+ color: var(--syn-input-color-focus);
47
49
  }
48
50
 
49
51
  .textarea--standard.textarea--focused:not(.textarea--disabled) .textarea__control {
@@ -53,8 +55,8 @@ export default css`
53
55
  .textarea--standard.textarea--disabled {
54
56
  background-color: var(--syn-input-background-color-disabled);
55
57
  border-color: var(--syn-input-border-color-disabled);
56
- opacity: 0.5;
57
58
  cursor: not-allowed;
59
+ opacity: var(--syn-input-disabled-opacity); /* #429: Use token for opacity */
58
60
  }
59
61
 
60
62
  .textarea__control,
@@ -63,9 +65,9 @@ export default css`
63
65
  }
64
66
 
65
67
  .textarea__size-adjuster {
66
- visibility: hidden;
67
- pointer-events: none;
68
68
  opacity: 0;
69
+ pointer-events: none;
70
+ visibility: hidden;
69
71
  }
70
72
 
71
73
  .textarea--standard.textarea--disabled .textarea__control {
@@ -78,8 +80,8 @@ export default css`
78
80
 
79
81
  /* Readonly textareas */
80
82
  .textarea--readonly {
81
- border: none;
82
83
  background-color: var(--syn-input-readonly-background-color);
84
+ border: none;
83
85
  color: var(--syn-input-color);
84
86
  }
85
87
 
@@ -95,21 +97,29 @@ export default css`
95
97
 
96
98
  .textarea--readonly.textarea--disabled {
97
99
  background-color: var(--syn-input-readonly-background-color-disabled);
98
- opacity: 0.5;
99
100
  cursor: not-allowed;
101
+ opacity: var(--syn-input-disabled-opacity);
100
102
  }
101
103
 
102
104
  .textarea__control {
105
+ -webkit-appearance: none;
106
+ appearance: none;
107
+ background: none;
108
+ border: none;
109
+ box-shadow: none;
110
+ color: var(--syn-input-color);
111
+ cursor: inherit;
103
112
  font-family: inherit;
104
113
  font-size: inherit;
105
114
  font-weight: inherit;
106
115
  line-height: 1.4;
107
- color: var(--syn-input-color);
108
- border: none;
109
- background: none;
110
- box-shadow: none;
111
- cursor: inherit;
112
- -webkit-appearance: none;
116
+
117
+ /*
118
+ Unfortunately we need to add a small margin to the textarea control. This is needed for the new sick 2025 theme,
119
+ as because of the big border-radius the resize icon will otherwise overlap with the border
120
+ This added margin needs to be subtracted from the padding of the textarea
121
+ */
122
+ margin: var(--syn-spacing-3x-small);
113
123
  }
114
124
 
115
125
  .textarea__control::-webkit-search-decoration,
@@ -117,12 +127,13 @@ export default css`
117
127
  .textarea__control::-webkit-search-results-button,
118
128
  .textarea__control::-webkit-search-results-decoration {
119
129
  -webkit-appearance: none;
130
+ appearance: none;
120
131
  }
121
132
 
122
133
  .textarea__control::placeholder {
123
134
  color: var(--syn-input-placeholder-color);
124
- user-select: none;
125
135
  -webkit-user-select: none;
136
+ user-select: none;
126
137
  }
127
138
 
128
139
  .textarea__control:focus {
@@ -132,14 +143,17 @@ export default css`
132
143
  /*
133
144
  * Size modifiers
134
145
  */
135
-
136
146
  .textarea--small {
137
147
  border-radius: var(--syn-input-border-radius-small);
138
148
  font-size: var(--syn-input-font-size-small);
139
149
  }
140
150
 
141
151
  .textarea--small .textarea__control {
142
- padding: 0.5em var(--syn-input-spacing-small);
152
+ /* TODO: Wait for Design response, how to handle this left / right spacing. Design has "--syn-input-spacing-medium" for all sizes,
153
+ but we have different (coming from shoelace) */
154
+
155
+ /* We need to subtract the added margin of the textarea control from the padding */
156
+ padding: calc(var(--syn-spacing-x-small) - var(--syn-spacing-3x-small)) calc(var(--syn-input-spacing-small) - var(--syn-spacing-3x-small));
143
157
  }
144
158
 
145
159
  .textarea--medium {
@@ -148,7 +162,8 @@ export default css`
148
162
  }
149
163
 
150
164
  .textarea--medium .textarea__control {
151
- padding: 0.5em var(--syn-input-spacing-medium);
165
+ /* We need to subtract the added margin of the textarea control from the padding */
166
+ padding: calc(var(--syn-spacing-x-small) - var(--syn-spacing-3x-small)) calc(var(--syn-input-spacing-medium) - var(--syn-spacing-3x-small));
152
167
  }
153
168
 
154
169
  .textarea--large {
@@ -157,13 +172,16 @@ export default css`
157
172
  }
158
173
 
159
174
  .textarea--large .textarea__control {
160
- padding: 0.5em var(--syn-input-spacing-large);
175
+ /* TODO: Wait for Design response, how to handle this left / right spacing. Design has "--syn-input-spacing-medium" for all sizes,
176
+ but we have different (coming from shoelace) */
177
+
178
+ /* We need to subtract the added margin of the textarea control from the padding */
179
+ padding: calc(var(--syn-spacing-x-small) - var(--syn-spacing-3x-small)) calc(var(--syn-input-spacing-large) - var(--syn-spacing-3x-small));
161
180
  }
162
181
 
163
182
  /*
164
183
  * Resize types
165
184
  */
166
-
167
185
  .textarea--resize-none .textarea__control {
168
186
  resize: none;
169
187
  }
@@ -174,7 +192,17 @@ export default css`
174
192
 
175
193
  .textarea--resize-auto .textarea__control {
176
194
  height: auto;
177
- resize: none;
178
195
  overflow-y: hidden;
196
+ resize: none;
197
+ }
198
+
199
+ /* Validation */
200
+ :host([data-user-invalid]) .textarea--standard {
201
+ border-color: var(--syn-input-border-color-focus-error);
202
+ }
203
+
204
+ :host([data-user-invalid]) .textarea--standard.textarea--focused:not(.textarea--disabled) {
205
+ border-color: var(--syn-input-border-color-focus-error);
206
+ box-shadow: 0 0 0 var(--syn-focus-ring-width) var(--syn-input-focus-ring-error);
179
207
  }
180
208
  `;
@@ -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 './textarea.styles.js';
16
- import customStyles from './textarea.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';
@@ -42,7 +40,7 @@ import { enableDefaultSettings } from '../../utilities/defaultSettings/decorator
42
40
  */
43
41
  @enableDefaultSettings('SynTextarea')
44
42
  export default class SynTextarea extends SynergyElement implements SynergyFormControl {
45
- static styles: CSSResultGroup = [componentStyles, formControlStyles, styles, formControlCustomStyles, customStyles];
43
+ static styles: CSSResultGroup = [componentStyles, formControlStyles, styles];
46
44
 
47
45
  private readonly formControlController = new FormControlController(this, {
48
46
  assumeInteractionOn: ['syn-blur', 'syn-input']
@@ -1,8 +1,7 @@
1
- /* eslint-disable */
2
1
  import { css } from 'lit';
3
2
 
4
3
  export default css`
5
- /* stylelint-disable */
4
+ /* stylelint-disable property-no-vendor-prefix */
6
5
  :host {
7
6
  --max-width: 20rem;
8
7
  --hide-delay: 0ms;
@@ -11,6 +10,11 @@ export default css`
11
10
  display: contents;
12
11
  }
13
12
 
13
+ /** #640: Adjust the zIndex of the arrow to make sure the box-shadow above does not bleed out */
14
+ :host ::part(arrow) {
15
+ z-index: 0 !important;
16
+ }
17
+
14
18
  .tooltip {
15
19
  --arrow-size: var(--syn-tooltip-arrow-size);
16
20
  --arrow-color: var(--syn-tooltip-background-color);
@@ -37,21 +41,22 @@ export default css`
37
41
  }
38
42
 
39
43
  .tooltip__body {
40
- display: block;
41
- width: max-content;
42
- max-width: var(--max-width);
43
- border-radius: var(--syn-tooltip-border-radius);
44
44
  background-color: var(--syn-tooltip-background-color);
45
+ border-radius: var(--syn-tooltip-border-radius);
46
+ box-shadow: var(--syn-shadow-large);
47
+ color: var(--syn-tooltip-color);
48
+ display: block;
45
49
  font-family: var(--syn-tooltip-font-family);
46
50
  font-size: var(--syn-tooltip-font-size);
47
51
  font-weight: var(--syn-tooltip-font-weight);
48
52
  line-height: var(--syn-tooltip-line-height);
49
- text-align: start;
50
- white-space: normal;
51
- color: var(--syn-tooltip-color);
53
+ max-width: var(--max-width);
52
54
  padding: var(--syn-tooltip-padding);
53
55
  pointer-events: none;
54
- user-select: none;
56
+ text-align: start;
55
57
  -webkit-user-select: none;
58
+ user-select: none;
59
+ white-space: normal;
60
+ width: max-content;
56
61
  }
57
62
  `;
@@ -4,15 +4,15 @@ import { classMap } from 'lit/directives/class-map.js';
4
4
  import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.js';
5
5
  import { html } from 'lit';
6
6
  import { LocalizeController } from '../../utilities/localize.js';
7
- import { property, query } from 'lit/decorators.js';
7
+ import { property, state, query } from 'lit/decorators.js';
8
8
  import { waitForEvent } from '../../internal/event.js';
9
9
  import { watch } from '../../internal/watch.js';
10
10
  import componentStyles from '../../styles/component.styles.js';
11
11
  import SynergyElement from '../../internal/synergy-element.js';
12
12
  import SynPopup from '../popup/popup.component.js';
13
13
  import styles from './tooltip.styles.js';
14
- import customStyles from './tooltip.custom.styles.js';
15
14
  import type { CSSResultGroup } from 'lit';
15
+ import type { VirtualElement } from '../popup/popup.component.js';
16
16
 
17
17
  /**
18
18
  * @summary Tooltips display additional information based on a specific action.
@@ -43,7 +43,7 @@ import type { CSSResultGroup } from 'lit';
43
43
  * @animation tooltip.hide - The animation to use when hiding the tooltip.
44
44
  */
45
45
  export default class SynTooltip extends SynergyElement {
46
- static styles: CSSResultGroup = [componentStyles, styles, customStyles];
46
+ static styles: CSSResultGroup = [componentStyles, styles];
47
47
  static dependencies = { 'syn-popup': SynPopup };
48
48
 
49
49
  private hoverTimeout: number;
@@ -54,6 +54,13 @@ export default class SynTooltip extends SynergyElement {
54
54
  @query('.tooltip__body') body: HTMLElement;
55
55
  @query('syn-popup') popup: SynPopup;
56
56
 
57
+ /**
58
+ * The element the tooltip will be anchored to. If the anchor lives outside of the tooltip, you can provide the anchor
59
+ * element `id`, a DOM element reference, or a `VirtualElement`. If the anchor lives inside the tooltip, use the
60
+ * `anchor` slot instead.
61
+ */
62
+ @state() private anchor: Element | string | VirtualElement | undefined = undefined;
63
+
57
64
  /** The tooltip's content. If you need to display HTML, use the `content` slot instead. */
58
65
  @property() content = '';
59
66
 
@@ -214,7 +221,7 @@ export default class SynTooltip extends SynergyElement {
214
221
  }
215
222
  }
216
223
 
217
- @watch(['content', 'distance','placement', 'skidding'])
224
+ @watch(['anchor', 'content', 'distance','placement', 'skidding'])
218
225
  async handleOptionsChange() {
219
226
  if (this.hasUpdated) {
220
227
  await this.updateComplete;
@@ -258,6 +265,7 @@ export default class SynTooltip extends SynergyElement {
258
265
  render() {
259
266
  return html`
260
267
  <syn-popup
268
+ .anchor=${this.anchor}
261
269
  part="base"
262
270
  exportparts="
263
271
  popup:base__popup,
@@ -275,7 +283,7 @@ export default class SynTooltip extends SynergyElement {
275
283
  arrow
276
284
  hover-bridge
277
285
  >
278
- ${'' /* eslint-disable-next-line lit-a11y/no-aria-slot */}
286
+ ${'' /* Always render slotted content in anchor position - it's always visible */}
279
287
  <slot slot="anchor" aria-describedby="tooltip"></slot>
280
288
 
281
289
  ${'' /* eslint-disable-next-line lit-a11y/accessible-name */}
@@ -22,16 +22,24 @@ import '@synergy-design-system/components/components/validate/validate.js';
22
22
  * and showing the validation message in a consistent, user defined way.
23
23
  * @documentation https://synergy-design-system.github.io/?path=/docs/components-syn-validate--docs
24
24
  * @dependency syn-alert
25
+ * @dependency syn-tooltip
25
26
  *
26
27
  * @slot - The form field that should be validated.
27
28
  * Avoid slotting in more than one element, as subsequent ones will be ignored.
28
29
  *
29
30
  * @csspart base - The component's base wrapper.
30
31
  * @csspart input-wrapper - The container that wraps the form field.
32
+ *
31
33
  * @csspart alert - The syn-alert that is shown when the variant is set to "inline".
32
34
  * @csspart alert__base - The container that wraps the alert.
33
35
  * @csspart alert__message - The container that wraps the alert message.
34
36
  * @csspart alert__icon - The container that wraps the alert icon.
37
+ *
38
+ * @csspart tooltip - The syn-tooltip that is shown when the variant is set to "tooltip".
39
+ * @csspart tooltip__base - The container that wraps the tooltip.
40
+ * @csspart tooltip__popup - The container that wraps the tooltip popup.
41
+ * @csspart tooltip__arrow - The container that wraps the tooltip arrow.
42
+ * @csspart tooltip__body - The container that wraps the tooltip body.
35
43
  */
36
44
  @Component({
37
45
  selector: 'syn-validate',
@@ -52,6 +60,7 @@ export class SynValidateComponent {
52
60
 
53
61
  The following variants are supported:
54
62
  - **native** (default): Uses the native browser validation, usually a browser tooltip.
63
+ - **tooltip**: Show the validation message as a tooltip using a `<syn-tooltip>`.
55
64
  - **inline**: Show the validation message underneath the element, using a `<syn-alert>`
56
65
  */
57
66
  @Input()
@@ -16,16 +16,24 @@ Component.define('syn-validate');
16
16
  * and showing the validation message in a consistent, user defined way.
17
17
  * @documentation https://synergy-design-system.github.io/?path=/docs/components-syn-validate--docs
18
18
  * @dependency syn-alert
19
+ * @dependency syn-tooltip
19
20
  *
20
21
  * @slot - The form field that should be validated.
21
22
  * Avoid slotting in more than one element, as subsequent ones will be ignored.
22
23
  *
23
24
  * @csspart base - The component's base wrapper.
24
25
  * @csspart input-wrapper - The container that wraps the form field.
26
+ *
25
27
  * @csspart alert - The syn-alert that is shown when the variant is set to "inline".
26
28
  * @csspart alert__base - The container that wraps the alert.
27
29
  * @csspart alert__message - The container that wraps the alert message.
28
30
  * @csspart alert__icon - The container that wraps the alert icon.
31
+ *
32
+ * @csspart tooltip - The syn-tooltip that is shown when the variant is set to "tooltip".
33
+ * @csspart tooltip__base - The container that wraps the tooltip.
34
+ * @csspart tooltip__popup - The container that wraps the tooltip popup.
35
+ * @csspart tooltip__arrow - The container that wraps the tooltip arrow.
36
+ * @csspart tooltip__body - The container that wraps the tooltip body.
29
37
  */
30
38
  export const SynValidate = createComponent({
31
39
  displayName: 'SynValidate',
@@ -1,14 +1,21 @@
1
1
  import type { CSSResultGroup, PropertyValues } from 'lit';
2
2
  import { html } from 'lit';
3
- import { property, queryAssignedElements, state } from 'lit/decorators.js';
3
+ import {
4
+ property,
5
+ query,
6
+ queryAssignedElements,
7
+ state,
8
+ } from 'lit/decorators.js';
4
9
  import { ifDefined } from 'lit/directives/if-defined.js';
5
10
  import componentStyles from '../../styles/component.styles.js';
6
11
  import SynergyElement from '../../internal/synergy-element.js';
7
12
  import type SynInput from '../input/input.component.js';
8
13
  import { watch } from '../../internal/watch.js';
9
14
  import SynAlert from '../alert/alert.component.js';
15
+ import SynTooltip from '../tooltip/tooltip.component.js';
10
16
  import {
11
17
  alertSizeForInput,
18
+ getActualInputElement,
12
19
  getEventNameForElement,
13
20
  isBlurEvent,
14
21
  isInvalidEvent,
@@ -18,29 +25,49 @@ import {
18
25
  import styles from './validate.styles.js';
19
26
  import { enableDefaultSettings } from '../../utilities/defaultSettings/decorator.js';
20
27
 
28
+ /**
29
+ * Utility function that renders the default slot content.
30
+ * This is needed to avoid code duplication when rendering the default slot in both the tooltip and the regular render function.
31
+ */
32
+ const renderDefaultSlot = () => html`
33
+ <slot
34
+ class="validate__input-wrapper"
35
+ part="input-wrapper"
36
+ ></slot>
37
+ `;
38
+
21
39
  /**
22
40
  * @summary Validate provides form field validation messages in a unified way.
23
41
  * It does this by using [the native browser validation](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation)
24
42
  * and showing the validation message in a consistent, user defined way.
25
43
  * @documentation https://synergy-design-system.github.io/?path=/docs/components-syn-validate--docs
26
44
  * @dependency syn-alert
45
+ * @dependency syn-tooltip
27
46
  *
28
47
  * @slot - The form field that should be validated.
29
48
  * Avoid slotting in more than one element, as subsequent ones will be ignored.
30
49
  *
31
50
  * @csspart base - The component's base wrapper.
32
51
  * @csspart input-wrapper - The container that wraps the form field.
52
+ *
33
53
  * @csspart alert - The syn-alert that is shown when the variant is set to "inline".
34
54
  * @csspart alert__base - The container that wraps the alert.
35
55
  * @csspart alert__message - The container that wraps the alert message.
36
56
  * @csspart alert__icon - The container that wraps the alert icon.
37
- */
57
+ *
58
+ * @csspart tooltip - The syn-tooltip that is shown when the variant is set to "tooltip".
59
+ * @csspart tooltip__base - The container that wraps the tooltip.
60
+ * @csspart tooltip__popup - The container that wraps the tooltip popup.
61
+ * @csspart tooltip__arrow - The container that wraps the tooltip arrow.
62
+ * @csspart tooltip__body - The container that wraps the tooltip body.
63
+ */
38
64
  @enableDefaultSettings('SynValidate')
39
65
  export default class SynValidate extends SynergyElement {
40
66
  static styles: CSSResultGroup = [componentStyles, styles];
41
67
 
42
68
  static dependencies = {
43
69
  'syn-alert': SynAlert,
70
+ 'syn-tooltip': SynTooltip,
44
71
  };
45
72
 
46
73
  controller = new AbortController();
@@ -51,6 +78,8 @@ export default class SynValidate extends SynergyElement {
51
78
 
52
79
  @queryAssignedElements() private slottedChildren: HTMLElement[];
53
80
 
81
+ @query('syn-tooltip') private tooltipElement?: SynTooltip;
82
+
54
83
  @state() validationMessage = '';
55
84
 
56
85
  @state() eagerFirstMount = true;
@@ -61,14 +90,17 @@ export default class SynValidate extends SynergyElement {
61
90
 
62
91
  @state() alertSize?: SynInput['size'];
63
92
 
93
+ @state() hasFocus = false;
94
+
64
95
  /**
65
96
  * The variant that should be used to show validation alerts.
66
97
  *
67
98
  * The following variants are supported:
68
99
  * - **native** (default): Uses the native browser validation, usually a browser tooltip.
100
+ * - **tooltip**: Show the validation message as a tooltip using a `<syn-tooltip>`.
69
101
  * - **inline**: Show the validation message underneath the element, using a `<syn-alert>`
70
102
  */
71
- @property({ reflect: true }) variant: 'native' | 'inline' = 'native';
103
+ @property({ reflect: true }) variant: 'native' | 'tooltip' | 'inline' = 'native';
72
104
 
73
105
  /** Do not show the error icon when using the inline variant validation */
74
106
  @property({ attribute: 'hide-icon', reflect: true, type: Boolean }) hideIcon = false;
@@ -235,6 +267,20 @@ export default class SynValidate extends SynergyElement {
235
267
  signal: this.controller.signal,
236
268
  });
237
269
  }
270
+
271
+ // #664: Add focus/blur listeners specifically for tooltip variant
272
+ // This is needed because we want to show the tooltip on focus and hide it on blur, but only when using the tooltip variant.
273
+ // Otherwise, this would interfere with the native validation tooltip, which also relies on focus and blur events.
274
+ const focusEvent = getEventNameForElement(input, 'focus');
275
+ const blurEvent = getEventNameForElement(input, 'blur');
276
+
277
+ input.addEventListener(focusEvent, this.handleInputFocus, {
278
+ signal: this.controller.signal,
279
+ });
280
+
281
+ input.addEventListener(blurEvent, this.handleInputBlur, {
282
+ signal: this.controller.signal,
283
+ });
238
284
  }
239
285
 
240
286
  private setValidationMessage(input: HTMLInputElement) {
@@ -266,6 +312,17 @@ export default class SynValidate extends SynergyElement {
266
312
  }
267
313
  };
268
314
 
315
+ /**
316
+ * Handle focus/blur events for tooltip variant
317
+ */
318
+ private handleInputFocus = () => {
319
+ this.hasFocus = true;
320
+ };
321
+
322
+ private handleInputBlur = () => {
323
+ this.hasFocus = false;
324
+ };
325
+
269
326
  /**
270
327
  * Handle the blur event during validation
271
328
  */
@@ -439,6 +496,36 @@ export default class SynValidate extends SynergyElement {
439
496
  this?.sizeObserver?.disconnect();
440
497
  }
441
498
 
499
+ updated(changedProperties: PropertyValues) {
500
+ super.updated(changedProperties);
501
+
502
+ // #664: Make sure to update the syn-tooltip if the validation state changes when using the tooltip variant
503
+ if (this.variant !== 'tooltip') {
504
+ return;
505
+ }
506
+
507
+ const tooltip = this.tooltipElement;
508
+
509
+ if (!tooltip) {
510
+ return;
511
+ }
512
+
513
+ // When we have a valid tooltip,
514
+ // we need to update the content and show or hide it based on the validation state and focus state.
515
+ // We have to do this manually, as there is a problem when updating open and content at the same time.
516
+ // The order is critical: fill before showing, don´t update the content during hide.
517
+ const shouldShowTooltip = !this.isValid && this.validationMessage && this.hasFocus;
518
+
519
+ if (shouldShowTooltip) {
520
+ tooltip.content = this.validationMessage;
521
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
522
+ tooltip.show();
523
+ } else {
524
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
525
+ tooltip.hide();
526
+ }
527
+ }
528
+
442
529
  private renderInlineValidation() {
443
530
  if (this.variant !== 'inline' || !this.validationMessage) {
444
531
  return '';
@@ -462,16 +549,27 @@ export default class SynValidate extends SynergyElement {
462
549
  }
463
550
 
464
551
  render() {
552
+ // #664: When using the tooltip variant, we need to wrap the default slot in a tooltip when the input is invalid and has a validation message.
553
+ const slotContent = this.variant === 'tooltip'
554
+ ? html`
555
+ <syn-tooltip
556
+ .anchor=${getActualInputElement(this.getInput()) as Element ?? undefined}
557
+ exportparts="base:tooltip__base,base__popup:tooltip__popup,base__arrow:tooltip__arrow,body:tooltip__body"
558
+ .open=${this.eager ? !this.isValid && this.validationMessage.length > 0 : false}
559
+ part="tooltip"
560
+ placement="bottom"
561
+ trigger="manual"
562
+ >
563
+ ${renderDefaultSlot()}
564
+ </syn-tooltip>
565
+ ` : renderDefaultSlot();
566
+
465
567
  return html`
466
568
  <div
467
569
  class="validate"
468
570
  part="base"
469
571
  >
470
- <slot
471
- class="validate__input-wrapper"
472
- part="input-wrapper"
473
- ></slot>
474
-
572
+ ${slotContent}
475
573
  ${this.renderInlineValidation()}
476
574
  </div>
477
575
  `;
@@ -11,16 +11,24 @@
11
11
  * and showing the validation message in a consistent, user defined way.
12
12
  * @documentation https://synergy-design-system.github.io/?path=/docs/components-syn-validate--docs
13
13
  * @dependency syn-alert
14
+ * @dependency syn-tooltip
14
15
  *
15
16
  * @slot - The form field that should be validated.
16
17
  * Avoid slotting in more than one element, as subsequent ones will be ignored.
17
18
  *
18
19
  * @csspart base - The component's base wrapper.
19
20
  * @csspart input-wrapper - The container that wraps the form field.
21
+ *
20
22
  * @csspart alert - The syn-alert that is shown when the variant is set to "inline".
21
23
  * @csspart alert__base - The container that wraps the alert.
22
24
  * @csspart alert__message - The container that wraps the alert message.
23
25
  * @csspart alert__icon - The container that wraps the alert icon.
26
+ *
27
+ * @csspart tooltip - The syn-tooltip that is shown when the variant is set to "tooltip".
28
+ * @csspart tooltip__base - The container that wraps the tooltip.
29
+ * @csspart tooltip__popup - The container that wraps the tooltip popup.
30
+ * @csspart tooltip__arrow - The container that wraps the tooltip arrow.
31
+ * @csspart tooltip__body - The container that wraps the tooltip body.
24
32
  */
25
33
  import { computed, ref } from 'vue';
26
34
  import '@synergy-design-system/components/components/validate/validate.js';
@@ -41,6 +49,7 @@ const props = defineProps<{
41
49
 
42
50
  The following variants are supported:
43
51
  - **native** (default): Uses the native browser validation, usually a browser tooltip.
52
+ - **tooltip**: Show the validation message as a tooltip using a `<syn-tooltip>`.
44
53
  - **inline**: Show the validation message underneath the element, using a `<syn-alert>`
45
54
  */
46
55
  variant?: SynValidate['variant'];