fluentui-webcomponents 0.0.1

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 (59) hide show
  1. package/AGENTS.md +212 -0
  2. package/README.md +99 -0
  3. package/components/avatar/fluent-avatar.css +481 -0
  4. package/components/avatar/fluent-avatar.js +80 -0
  5. package/components/badge/fluent-badge.css +289 -0
  6. package/components/badge/fluent-badge.js +20 -0
  7. package/components/breadcrumb/fluent-breadcrumb.css +29 -0
  8. package/components/breadcrumb/fluent-breadcrumb.js +33 -0
  9. package/components/breadcrumb-item/fluent-breadcrumb-item.css +70 -0
  10. package/components/breadcrumb-item/fluent-breadcrumb-item.js +77 -0
  11. package/components/button/fluent-button.css +265 -0
  12. package/components/button/fluent-button.js +326 -0
  13. package/components/card/fluent-card.css +85 -0
  14. package/components/card/fluent-card.js +21 -0
  15. package/components/checkbox/fluent-checkbox.css +171 -0
  16. package/components/checkbox/fluent-checkbox.js +294 -0
  17. package/components/dialog/fluent-dialog.css +82 -0
  18. package/components/dialog/fluent-dialog.js +137 -0
  19. package/components/divider/fluent-divider.css +124 -0
  20. package/components/divider/fluent-divider.js +14 -0
  21. package/components/image/fluent-image.css +73 -0
  22. package/components/image/fluent-image.js +36 -0
  23. package/components/label/fluent-label.css +49 -0
  24. package/components/label/fluent-label.js +61 -0
  25. package/components/link/fluent-link.css +72 -0
  26. package/components/link/fluent-link.js +109 -0
  27. package/components/menu/fluent-menu.css +57 -0
  28. package/components/menu/fluent-menu.js +202 -0
  29. package/components/menu-item/fluent-menu-item.css +152 -0
  30. package/components/menu-item/fluent-menu-item.js +177 -0
  31. package/components/popover/fluent-popover.css +95 -0
  32. package/components/popover/fluent-popover.js +93 -0
  33. package/components/radio/fluent-radio.css +123 -0
  34. package/components/radio/fluent-radio.js +257 -0
  35. package/components/select/fluent-select.css +194 -0
  36. package/components/select/fluent-select.js +245 -0
  37. package/components/slider/fluent-slider.css +199 -0
  38. package/components/slider/fluent-slider.js +438 -0
  39. package/components/spinner/fluent-spinner.css +160 -0
  40. package/components/spinner/fluent-spinner.js +30 -0
  41. package/components/switch/fluent-switch.css +154 -0
  42. package/components/switch/fluent-switch.js +260 -0
  43. package/components/text/fluent-text.css +128 -0
  44. package/components/text/fluent-text.js +21 -0
  45. package/components/text-input/fluent-text-input.css +227 -0
  46. package/components/text-input/fluent-text-input.js +298 -0
  47. package/components/textarea/fluent-textarea.css +227 -0
  48. package/components/textarea/fluent-textarea.js +400 -0
  49. package/components/tooltip/fluent-tooltip.css +65 -0
  50. package/components/tooltip/fluent-tooltip.js +102 -0
  51. package/components/tree/fluent-tree.css +16 -0
  52. package/components/tree/fluent-tree.js +167 -0
  53. package/components/tree-item/fluent-tree-item.css +147 -0
  54. package/components/tree-item/fluent-tree-item.js +163 -0
  55. package/core/fluent-element.js +34 -0
  56. package/gallery.html +492 -0
  57. package/package.json +19 -0
  58. package/theme/theme-picker.js +38 -0
  59. package/tokens.css +724 -0
@@ -0,0 +1,154 @@
1
+ @import url('../../tokens.css');
2
+
3
+ :host { display: inline-flex; }
4
+
5
+ :host([hidden]) .root {
6
+ display: none;
7
+ }
8
+
9
+ .root {
10
+ box-sizing: border-box;
11
+ align-items: center;
12
+ flex-direction: row;
13
+ outline: none;
14
+ user-select: none;
15
+ padding: 0 var(--spacingHorizontalXXS);
16
+ width: 40px;
17
+ height: 20px;
18
+ background-color: var(--colorTransparentBackground);
19
+ border: 1px solid var(--colorNeutralStrokeAccessible);
20
+ border-radius: var(--borderRadiusCircular);
21
+ display: inline-flex;
22
+ position: relative;
23
+ }
24
+
25
+ :host(:enabled) .root {
26
+ cursor: pointer;
27
+ }
28
+
29
+ .root:hover {
30
+ background: none;
31
+ border-color: var(--colorNeutralStrokeAccessibleHover);
32
+ }
33
+
34
+ .root:active {
35
+ border-color: var(--colorNeutralStrokeAccessiblePressed);
36
+ }
37
+
38
+ :host([disabled]) .root,
39
+ :host([readonly]) .root {
40
+ border: 1px solid var(--colorNeutralStrokeDisabled);
41
+ background-color: transparent;
42
+ cursor: default;
43
+ }
44
+
45
+ :host([checked]) .root {
46
+ background: var(--colorCompoundBrandBackground);
47
+ border-color: var(--colorCompoundBrandBackground);
48
+ }
49
+
50
+ :host([checked]) .root:hover {
51
+ background: var(--colorCompoundBrandBackgroundHover);
52
+ border-color: var(--colorCompoundBrandBackgroundHover);
53
+ }
54
+
55
+ :host([checked]) .root:active {
56
+ background: var(--colorCompoundBrandBackgroundPressed);
57
+ border-color: var(--colorCompoundBrandBackgroundPressed);
58
+ }
59
+
60
+ :host([checked][disabled]) .root {
61
+ background: var(--colorNeutralBackgroundDisabled);
62
+ border-color: var(--colorNeutralStrokeDisabled);
63
+ }
64
+
65
+ .checked-indicator {
66
+ height: 14px;
67
+ width: 14px;
68
+ border-radius: 50%;
69
+ margin: auto 0 auto 0;
70
+ background-color: var(--colorNeutralForeground3);
71
+ transition-duration: var(--durationNormal);
72
+ transition-timing-function: var(--curveEasyEase);
73
+ transition-property: margin-left, margin-right;
74
+ display: block;
75
+ flex-shrink: 0;
76
+ }
77
+
78
+ :host([checked]) .root .checked-indicator {
79
+ background-color: var(--colorNeutralForegroundInverted);
80
+ margin-left: calc(100% - 14px);
81
+ margin-right: 0;
82
+ }
83
+
84
+ :host([checked]) .root:hover .checked-indicator {
85
+ background: var(--colorNeutralForegroundInvertedHover);
86
+ }
87
+
88
+ :host([checked]) .root:active .checked-indicator {
89
+ background: var(--colorNeutralForegroundInvertedPressed);
90
+ }
91
+
92
+ .root:hover .checked-indicator {
93
+ background-color: var(--colorNeutralForeground3Hover);
94
+ }
95
+
96
+ .root:active .checked-indicator {
97
+ background-color: var(--colorNeutralForeground3Pressed);
98
+ }
99
+
100
+ :host([disabled]) .root .checked-indicator,
101
+ :host([readonly]) .root .checked-indicator {
102
+ background: var(--colorNeutralForegroundDisabled);
103
+ }
104
+
105
+ :host([checked][disabled]) .root .checked-indicator {
106
+ background: var(--colorNeutralForegroundDisabled);
107
+ }
108
+
109
+ .root:focus-visible {
110
+ outline: none;
111
+ }
112
+
113
+ :host(:not([slot='input'])) .root:focus-visible {
114
+ border-color: var(--colorTransparentStroke);
115
+ outline: var(--strokeWidthThick) solid var(--colorTransparentStroke);
116
+ outline-offset: 1px;
117
+ box-shadow: var(--shadow4), 0 0 0 2px var(--colorStrokeFocus2);
118
+ }
119
+
120
+ @media (forced-colors: active) {
121
+ .root {
122
+ border-color: InactiveBorder;
123
+ }
124
+
125
+ :host([checked]) .root,
126
+ :host([checked]) .root:active,
127
+ :host([checked]) .root:hover {
128
+ background: Highlight;
129
+ border-color: Highlight;
130
+ }
131
+
132
+ .checked-indicator,
133
+ .root:hover .checked-indicator,
134
+ .root:active .checked-indicator {
135
+ background-color: ActiveCaption;
136
+ }
137
+
138
+ :host([checked]) .root .checked-indicator,
139
+ :host([checked]) .root:hover .checked-indicator,
140
+ :host([checked]) .root:active .checked-indicator {
141
+ background-color: ButtonFace;
142
+ }
143
+
144
+ :host([disabled]) .root .checked-indicator,
145
+ :host([checked][disabled]) .root .checked-indicator {
146
+ background-color: GrayText;
147
+ }
148
+ }
149
+
150
+ @media screen and (prefers-reduced-motion: reduce) {
151
+ .checked-indicator {
152
+ transition-duration: 0.01ms;
153
+ }
154
+ }
@@ -0,0 +1,260 @@
1
+ import { FluentElement } from '../../core/fluent-element.js';
2
+
3
+ const stylesUrl = new URL('./fluent-switch.css', import.meta.url).href;
4
+
5
+ class FluentSwitch extends FluentElement {
6
+ static stylesUrl = stylesUrl;
7
+ static formAssociated = true;
8
+
9
+ static template = `
10
+ <div class="root">
11
+ <slot name="switch">
12
+ <span class="checked-indicator" part="checked-indicator"></span>
13
+ </slot>
14
+ </div>
15
+ `;
16
+
17
+ static get observedAttributes() {
18
+ return ['checked', 'disabled', 'required', 'value', 'name', 'label-position', 'autofocus', 'size', 'shape'];
19
+ }
20
+
21
+ constructor() {
22
+ super();
23
+ this._internals = this.attachInternals();
24
+ this._internals.role = 'switch';
25
+ this._checked = false;
26
+ this._dirtyChecked = false;
27
+ this._keydownPressed = false;
28
+ this._value = 'on';
29
+ this._name = '';
30
+ this._size = 'medium';
31
+ this._shape = 'circular';
32
+ }
33
+
34
+ connectedCallback() {
35
+ super.connectedCallback();
36
+ this._updateDisabled();
37
+ this._setAriaChecked();
38
+ this.addEventListener('click', this._clickHandler.bind(this));
39
+ this.addEventListener('keydown', this._keydownHandler.bind(this));
40
+ this.addEventListener('keyup', this._keyupHandler.bind(this));
41
+ }
42
+
43
+ changed(name, oldVal, newVal) {
44
+ switch (name) {
45
+ case 'checked':
46
+ if (!this._dirtyChecked) {
47
+ this._checked = newVal !== null;
48
+ this._setAriaChecked();
49
+ }
50
+ break;
51
+ case 'disabled':
52
+ this._updateDisabled();
53
+ break;
54
+ case 'required':
55
+ this._internals.ariaRequired = this.required ? 'true' : 'false';
56
+ this._setValidity();
57
+ break;
58
+ case 'value':
59
+ this._value = newVal || 'on';
60
+ break;
61
+ case 'name':
62
+ this._name = newVal || '';
63
+ if (this._name) {
64
+ this.setAttribute('name', this._name);
65
+ } else {
66
+ this.removeAttribute('name');
67
+ }
68
+ break;
69
+ case 'label-position':
70
+ break;
71
+ case 'autofocus':
72
+ if (newVal !== null && !this.disabled) {
73
+ this.focus();
74
+ }
75
+ break;
76
+ case 'size':
77
+ this._size = newVal || 'medium';
78
+ break;
79
+ case 'shape':
80
+ this._shape = newVal || 'circular';
81
+ break;
82
+ }
83
+ }
84
+
85
+ get checked() {
86
+ return this._checked;
87
+ }
88
+
89
+ set checked(val) {
90
+ this._checked = !!val;
91
+ this._setAriaChecked();
92
+ this._setFormValue(this._checked ? this._value : null);
93
+ this._setValidity();
94
+ if (this._checked) {
95
+ this.setAttribute('checked', '');
96
+ } else {
97
+ this.removeAttribute('checked');
98
+ }
99
+ }
100
+
101
+ get disabled() {
102
+ return this.hasAttribute('disabled');
103
+ }
104
+
105
+ set disabled(val) {
106
+ if (val) {
107
+ this.setAttribute('disabled', '');
108
+ } else {
109
+ this.removeAttribute('disabled');
110
+ }
111
+ }
112
+
113
+ get required() {
114
+ return this.hasAttribute('required');
115
+ }
116
+
117
+ get value() {
118
+ return this._value || 'on';
119
+ }
120
+
121
+ set value(val) {
122
+ this._value = val;
123
+ if (this._checked) {
124
+ this._setFormValue(val);
125
+ }
126
+ }
127
+
128
+ get name() {
129
+ return this._name;
130
+ }
131
+
132
+ set name(val) {
133
+ this._name = val;
134
+ if (val) {
135
+ this.setAttribute('name', val);
136
+ } else {
137
+ this.removeAttribute('name');
138
+ }
139
+ }
140
+
141
+ get form() {
142
+ return this._internals.form;
143
+ }
144
+
145
+ get labels() {
146
+ return Object.freeze(Array.from(this._internals.labels));
147
+ }
148
+
149
+ get validity() {
150
+ return this._internals.validity;
151
+ }
152
+
153
+ get validationMessage() {
154
+ if (this._internals.validationMessage) {
155
+ return this._internals.validationMessage;
156
+ }
157
+ if (!this._validationFallbackMessage) {
158
+ const el = document.createElement('input');
159
+ el.type = 'checkbox';
160
+ el.required = true;
161
+ el.checked = false;
162
+ this._validationFallbackMessage = el.validationMessage;
163
+ }
164
+ return this._validationFallbackMessage;
165
+ }
166
+
167
+ get willValidate() {
168
+ return this._internals.willValidate;
169
+ }
170
+
171
+ toggleChecked(force) {
172
+ this._checked = typeof force === 'boolean' ? force : !this._checked;
173
+ this._dirtyChecked = true;
174
+ this._setAriaChecked();
175
+ this._setFormValue(this._checked ? this._value : null);
176
+ this._setValidity();
177
+ if (this._checked) {
178
+ this.setAttribute('checked', '');
179
+ } else {
180
+ this.removeAttribute('checked');
181
+ }
182
+ }
183
+
184
+ checkValidity() {
185
+ return this._internals.checkValidity();
186
+ }
187
+
188
+ reportValidity() {
189
+ return this._internals.reportValidity();
190
+ }
191
+
192
+ setCustomValidity(message) {
193
+ this._internals.setValidity({ customError: !!message }, message);
194
+ this._setValidity();
195
+ }
196
+
197
+ _clickHandler(e) {
198
+ if (this.disabled) return;
199
+ this._dirtyChecked = true;
200
+ const prev = this._checked;
201
+ this.toggleChecked();
202
+ if (prev !== this._checked) {
203
+ this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
204
+ this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
205
+ }
206
+ }
207
+
208
+ _keydownHandler(e) {
209
+ if (e.key === ' ') {
210
+ e.preventDefault();
211
+ this._keydownPressed = true;
212
+ }
213
+ }
214
+
215
+ _keyupHandler(e) {
216
+ if (!this._keydownPressed || e.key !== ' ') return;
217
+ this._keydownPressed = false;
218
+ this.click();
219
+ }
220
+
221
+ _updateDisabled() {
222
+ const d = this.hasAttribute('disabled');
223
+ if (d) {
224
+ this.removeAttribute('tabindex');
225
+ } else {
226
+ const t = this.getAttribute('tabindex');
227
+ this.tabIndex = Number(t ?? 0) < 0 ? -1 : 0;
228
+ }
229
+ this._internals.ariaDisabled = d ? 'true' : 'false';
230
+ }
231
+
232
+ _setAriaChecked() {
233
+ this._internals.ariaChecked = this._checked ? 'true' : 'false';
234
+ }
235
+
236
+ _setFormValue(value) {
237
+ this._internals.setFormValue(value, value);
238
+ }
239
+
240
+ _setValidity() {
241
+ if (this.disabled || !this.required) {
242
+ this._internals.setValidity({});
243
+ return;
244
+ }
245
+ const msg = this.validationMessage;
246
+ this._internals.setValidity(
247
+ { valueMissing: !!this.required && !this._checked },
248
+ msg || undefined
249
+ );
250
+ }
251
+
252
+ formResetCallback() {
253
+ this._checked = this.hasAttribute('checked');
254
+ this._dirtyChecked = false;
255
+ this._setAriaChecked();
256
+ this._setValidity();
257
+ }
258
+ }
259
+
260
+ customElements.define('fluent-switch', FluentSwitch);
@@ -0,0 +1,128 @@
1
+ @import url('../../tokens.css');
2
+
3
+ :host { display: inline; }
4
+
5
+ .root {
6
+ display: inline;
7
+ font-family: var(--fontFamilyBase);
8
+ font-size: var(--fontSizeBase300);
9
+ line-height: var(--lineHeightBase300);
10
+ font-weight: var(--fontWeightRegular);
11
+ text-align: start;
12
+ }
13
+
14
+ :host([nowrap]) .root,
15
+ :host([nowrap]) .root ::slotted(*) {
16
+ white-space: nowrap;
17
+ overflow: hidden;
18
+ }
19
+
20
+ :host([truncate]) .root,
21
+ :host([truncate]) .root ::slotted(*) {
22
+ text-overflow: ellipsis;
23
+ }
24
+
25
+ :host([block]) .root {
26
+ display: block;
27
+ }
28
+
29
+ :host([italic]) .root {
30
+ font-style: italic;
31
+ }
32
+
33
+ :host([underline]) .root {
34
+ text-decoration-line: underline;
35
+ }
36
+
37
+ :host([strikethrough]) .root {
38
+ text-decoration-line: line-through;
39
+ }
40
+
41
+ :host([underline][strikethrough]) .root {
42
+ text-decoration-line: line-through underline;
43
+ }
44
+
45
+ :host([size='100']) .root {
46
+ font-size: var(--fontSizeBase100);
47
+ line-height: var(--lineHeightBase100);
48
+ }
49
+
50
+ :host([size='200']) .root {
51
+ font-size: var(--fontSizeBase200);
52
+ line-height: var(--lineHeightBase200);
53
+ }
54
+
55
+ :host([size='400']) .root {
56
+ font-size: var(--fontSizeBase400);
57
+ line-height: var(--lineHeightBase400);
58
+ }
59
+
60
+ :host([size='500']) .root {
61
+ font-size: var(--fontSizeBase500);
62
+ line-height: var(--lineHeightBase500);
63
+ }
64
+
65
+ :host([size='600']) .root {
66
+ font-size: var(--fontSizeBase600);
67
+ line-height: var(--lineHeightBase600);
68
+ }
69
+
70
+ :host([size='700']) .root {
71
+ font-size: var(--fontSizeBase700);
72
+ line-height: var(--lineHeightBase700);
73
+ }
74
+
75
+ :host([size='800']) .root {
76
+ font-size: var(--fontSizeBase800);
77
+ line-height: var(--lineHeightBase800);
78
+ }
79
+
80
+ :host([size='900']) .root {
81
+ font-size: var(--fontSizeBase900);
82
+ line-height: var(--lineHeightBase900);
83
+ }
84
+
85
+ :host([size='1000']) .root {
86
+ font-size: var(--fontSizeBase1000);
87
+ line-height: var(--lineHeightBase1000);
88
+ }
89
+
90
+ :host([font='monospace']) .root {
91
+ font-family: var(--fontFamilyMonospace);
92
+ }
93
+
94
+ :host([font='numeric']) .root {
95
+ font-family: var(--fontFamilyNumeric);
96
+ }
97
+
98
+ :host([weight='medium']) .root {
99
+ font-weight: var(--fontWeightMedium);
100
+ }
101
+
102
+ :host([weight='semibold']) .root {
103
+ font-weight: var(--fontWeightSemibold);
104
+ }
105
+
106
+ :host([weight='bold']) .root {
107
+ font-weight: var(--fontWeightBold);
108
+ }
109
+
110
+ :host([align='center']) .root {
111
+ text-align: center;
112
+ }
113
+
114
+ :host([align='end']) .root {
115
+ text-align: end;
116
+ }
117
+
118
+ :host([align='justify']) .root {
119
+ text-align: justify;
120
+ }
121
+
122
+ ::slotted(*) {
123
+ font: inherit;
124
+ line-height: inherit;
125
+ text-decoration-line: inherit;
126
+ text-align: inherit;
127
+ margin: 0;
128
+ }
@@ -0,0 +1,21 @@
1
+ import { FluentElement } from '../../core/fluent-element.js';
2
+
3
+ const stylesUrl = new URL('./fluent-text.css', import.meta.url).href;
4
+
5
+ class FluentText extends FluentElement {
6
+ static stylesUrl = stylesUrl;
7
+ static template = `<div class="root"><slot></slot></div>`;
8
+
9
+ static formAssociated = true;
10
+
11
+ constructor() {
12
+ super();
13
+ this._internals = this.attachInternals();
14
+ }
15
+
16
+ static get observedAttributes() {
17
+ return ['size', 'weight', 'font', 'align', 'nowrap', 'truncate', 'italic', 'underline', 'strikethrough', 'block'];
18
+ }
19
+ }
20
+
21
+ customElements.define('fluent-text', FluentText);