@muonic/muon 0.0.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nycrc +17 -0
- package/.versionrc +3 -0
- package/CHANGELOG.md +389 -0
- package/components/card/index.js +1 -0
- package/components/card/src/card-component.js +43 -0
- package/components/card/src/card-styles.css +25 -0
- package/components/card/src/config-tokens.json +11 -0
- package/components/card/src/design-tokens.json +34 -0
- package/components/card/story.js +52 -0
- package/components/cta/index.js +1 -0
- package/components/cta/src/config-tokens.json +11 -0
- package/components/cta/src/cta-component.js +174 -0
- package/components/cta/src/cta-styles.css +105 -0
- package/components/cta/src/design-tokens.json +132 -0
- package/components/cta/story.js +99 -0
- package/components/detail/index.js +1 -0
- package/components/detail/src/config-tokens.json +11 -0
- package/components/detail/src/design-tokens.json +102 -0
- package/components/detail/src/detail-component.js +27 -0
- package/components/detail/src/detail-styles.css +83 -0
- package/components/detail/story.js +33 -0
- package/components/form/index.js +1 -0
- package/components/form/src/config-tokens.json +11 -0
- package/components/form/src/design-tokens.json +9 -0
- package/components/form/src/form-component.js +197 -0
- package/components/form/src/form-styles.css +10 -0
- package/components/form/story.js +71 -0
- package/components/icon/index.js +1 -0
- package/components/icon/src/config-tokens.json +31 -0
- package/components/icon/src/design-tokens.json +8 -0
- package/components/icon/src/icon-component.js +91 -0
- package/components/icon/src/icon-styles.css +26 -0
- package/components/icon/story.js +26 -0
- package/components/image/index.js +1 -0
- package/components/image/src/config-tokens.json +26 -0
- package/components/image/src/image-component.js +96 -0
- package/components/image/src/image-styles.css +71 -0
- package/components/image/story.js +31 -0
- package/components/inputter/index.js +1 -0
- package/components/inputter/src/config-tokens.json +14 -0
- package/components/inputter/src/design-tokens.json +308 -0
- package/components/inputter/src/inputter-component.js +227 -0
- package/components/inputter/src/inputter-styles-detail.css +59 -0
- package/components/inputter/src/inputter-styles.css +305 -0
- package/components/inputter/src/inputter-styles.slotted.css +64 -0
- package/components/inputter/story.js +243 -0
- package/css/accessibility.css +3 -0
- package/css/default.css +9 -0
- package/css/global.css +8 -0
- package/directives/image-loader-directive.js +116 -0
- package/directives/svg-loader-directive.js +94 -0
- package/index.js +52 -0
- package/mixins/card-mixin.js +27 -0
- package/mixins/detail-mixin.js +128 -0
- package/mixins/form-associate-mixin.js +36 -0
- package/mixins/form-element-mixin.js +378 -0
- package/mixins/image-holder-mixin.js +20 -0
- package/mixins/mask-mixin.js +159 -0
- package/mixins/validation-mixin.js +272 -0
- package/muon-element/index.js +97 -0
- package/package.json +72 -0
- package/rollup.config.mjs +30 -0
- package/scripts/build/storybook/index.mjs +11 -0
- package/scripts/build/storybook/run.mjs +47 -0
- package/scripts/rollup-plugins.mjs +116 -0
- package/scripts/serve/index.mjs +11 -0
- package/scripts/serve/run.mjs +27 -0
- package/scripts/style-dictionary.mjs +64 -0
- package/scripts/utils/config.mjs +30 -0
- package/scripts/utils/index.mjs +283 -0
- package/storybook/find-stories.js +36 -0
- package/storybook/server.config.mjs +19 -0
- package/storybook/stories.js +86 -0
- package/storybook/tokens/color.js +87 -0
- package/storybook/tokens/font.js +52 -0
- package/storybook/tokens/spacer.js +48 -0
- package/tests/README.md +3 -0
- package/tests/components/card/__snapshots__/card.test.snap.js +70 -0
- package/tests/components/card/card.test.js +81 -0
- package/tests/components/cta/__snapshots__/cta.test.snap.js +246 -0
- package/tests/components/cta/cta.test.js +212 -0
- package/tests/components/form/__snapshots__/form.test.snap.js +115 -0
- package/tests/components/form/form.test.js +336 -0
- package/tests/components/icon/__snapshots__/icon.test.snap.js +95 -0
- package/tests/components/icon/icon.test.js +197 -0
- package/tests/components/image/__snapshots__/image.test.snap.js +205 -0
- package/tests/components/image/image.test.js +314 -0
- package/tests/components/image/images/15.png +0 -0
- package/tests/components/image/images/150.png +0 -0
- package/tests/components/inputter/__snapshots__/inputter.test.snap.js +357 -0
- package/tests/components/inputter/inputter.test.js +427 -0
- package/tests/helpers/index.js +30 -0
- package/tests/mixins/__snapshots__/card.test.snap.js +35 -0
- package/tests/mixins/__snapshots__/detail.test.snap.js +237 -0
- package/tests/mixins/__snapshots__/form-element.test.snap.js +137 -0
- package/tests/mixins/__snapshots__/mask.test.snap.js +53 -0
- package/tests/mixins/__snapshots__/validation.test.snap.js +297 -0
- package/tests/mixins/card.test.js +63 -0
- package/tests/mixins/detail.test.js +223 -0
- package/tests/mixins/form-element.test.js +473 -0
- package/tests/mixins/mask.test.js +261 -0
- package/tests/mixins/muon-element.test.js +52 -0
- package/tests/mixins/validation.test.js +423 -0
- package/tests/runner/commands.mjs +19 -0
- package/tests/scripts/utils/card-component.js +26 -0
- package/tests/scripts/utils/muon.config.test.json +13 -0
- package/tests/scripts/utils/single.component.config.json +5 -0
- package/tests/scripts/utils/test-runner.mjs +1 -0
- package/tests/scripts/utils/utils-test.mjs +284 -0
- package/tests/utils/validation.functions.test.js +199 -0
- package/tokens/theme/color.json +482 -0
- package/tokens/theme/font.json +61 -0
- package/tokens/theme/size.json +27 -0
- package/tokens/theme/spacer.json +73 -0
- package/tokens/utils/formats/reference.js +17 -0
- package/tokens/utils/modular-scale.js +33 -0
- package/tokens/utils/templates/font-face.css.template +30 -0
- package/tokens/utils/transforms/color.js +27 -0
- package/tokens/utils/transforms/string.js +6 -0
- package/tokens/utils/validation.json +76 -0
- package/utils/scroll/index.js +31 -0
- package/utils/validation/index.js +205 -0
- package/web-test-runner.browserstack.config.mjs +123 -0
- package/web-test-runner.config.mjs +44 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { dedupeMixin } from '@muonic/muon';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A mixin to associate the component to the enclosing native form.
|
|
5
|
+
*
|
|
6
|
+
* @mixin FormElementMixin
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const FormAssociateMixin = dedupeMixin((superClass) =>
|
|
10
|
+
class FormAssociateMixinClass extends superClass {
|
|
11
|
+
|
|
12
|
+
static get properties() {
|
|
13
|
+
return {
|
|
14
|
+
_internals: {
|
|
15
|
+
type: Object,
|
|
16
|
+
state: true
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static get formAssociated() {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
constructor() {
|
|
26
|
+
super();
|
|
27
|
+
this._internals = this.attachInternals();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
updated(changedProperties) {
|
|
31
|
+
if (changedProperties.has('value')) {
|
|
32
|
+
this._internals.setFormValue(this.value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
);
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import { html, MuonElement, dedupeMixin } from '@muonic/muon';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A mixin to hold base setup for a form element.
|
|
5
|
+
*
|
|
6
|
+
* @mixin FormElementMixin
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const FormElementMixin = dedupeMixin((superClass) =>
|
|
10
|
+
class FormElementMixinClass extends superClass {
|
|
11
|
+
|
|
12
|
+
static get properties() {
|
|
13
|
+
return {
|
|
14
|
+
name: {
|
|
15
|
+
type: String,
|
|
16
|
+
reflect: true
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
value: {
|
|
20
|
+
type: String,
|
|
21
|
+
reflect: true
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
heading: {
|
|
25
|
+
type: String
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
labelID: {
|
|
29
|
+
type: String
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
_inputElement: {
|
|
33
|
+
type: Boolean,
|
|
34
|
+
state: true
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
_id: {
|
|
38
|
+
type: String,
|
|
39
|
+
state: true
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
_inputTypes: {
|
|
43
|
+
type: Object,
|
|
44
|
+
state: true
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
constructor() {
|
|
50
|
+
super();
|
|
51
|
+
|
|
52
|
+
this._inputTypes = {
|
|
53
|
+
RADIO: 'radio',
|
|
54
|
+
CHECKBOX: 'checkbox',
|
|
55
|
+
SELECT: 'select',
|
|
56
|
+
SEARCH: 'search',
|
|
57
|
+
DATE: 'date',
|
|
58
|
+
SINGLE: 'single'
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
this.value = '';
|
|
62
|
+
this.labelID = '';
|
|
63
|
+
this.heading = '';
|
|
64
|
+
this._inputElement = true;
|
|
65
|
+
this._id = `${this._randomId}-input`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static get shadowRootOptions() {
|
|
69
|
+
return { ...MuonElement.shadowRootOptions, delegatesFocus: true };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* A method to generate random Id for html elements.
|
|
74
|
+
*
|
|
75
|
+
* @returns {string} - Random generated id.
|
|
76
|
+
* @protected
|
|
77
|
+
*/
|
|
78
|
+
get _randomId() {
|
|
79
|
+
return `mnid-${Math.random().toString(36).substring(2, 15)}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* A method to get input type from the slotted html form elements.
|
|
84
|
+
*
|
|
85
|
+
* @returns {string} Input type.
|
|
86
|
+
* @protected
|
|
87
|
+
*/
|
|
88
|
+
get _inputType() {
|
|
89
|
+
const inputType = this.querySelector('input')?.type;
|
|
90
|
+
if (inputType && Object.values(this._inputTypes).indexOf(inputType) > -1) {
|
|
91
|
+
return inputType;
|
|
92
|
+
} else if (this.querySelector('select')) {
|
|
93
|
+
return this._inputTypes.SELECT;
|
|
94
|
+
}
|
|
95
|
+
return this._inputTypes.SINGLE;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
firstUpdated() {
|
|
99
|
+
super.firstUpdated();
|
|
100
|
+
if (!this.name) {
|
|
101
|
+
this.name = this._slottedInputs?.[0]?.name ?? '';
|
|
102
|
+
}
|
|
103
|
+
if (!this._isMultiple) {
|
|
104
|
+
if (this.labelID?.length > 0) {
|
|
105
|
+
this._slottedInputs.forEach((slot) => {
|
|
106
|
+
slot.setAttribute('aria-labelledby', this.labelID);
|
|
107
|
+
});
|
|
108
|
+
} else {
|
|
109
|
+
this._id = this._slottedInputs[0]?.getAttribute('id') || this._id;
|
|
110
|
+
this._slottedInputs[0]?.setAttribute('id', this._id);
|
|
111
|
+
this._slottedLabel?.setAttribute('for', this._id);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
this.__syncValue(true);
|
|
115
|
+
|
|
116
|
+
this._boundChangeEvent = (changeEvent) => {
|
|
117
|
+
this._onChange(changeEvent);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
this._boundBlurEvent = (blurEvent) => {
|
|
121
|
+
this._onBlur(blurEvent);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
this._boundInputEvent = (inputEvent) => {
|
|
125
|
+
this._onInput(inputEvent);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
this._slottedInputs.forEach((input) => {
|
|
129
|
+
input.addEventListener('change', this._boundChangeEvent);
|
|
130
|
+
input.addEventListener('blur', this._boundBlurEvent);
|
|
131
|
+
input.addEventListener('input', this._boundInputEvent);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
focus() {
|
|
136
|
+
this.updateComplete.then(() => {
|
|
137
|
+
this._slottedInputs[0].focus();
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
reset() {
|
|
142
|
+
this.value = this._slottedValue; // get values from slotted html form elements
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* A method to sync the value property of the component with value of slotted input elements.
|
|
146
|
+
*
|
|
147
|
+
* @param {boolean} firstSync - If first time syncing values.
|
|
148
|
+
* @returns {void}
|
|
149
|
+
* @private
|
|
150
|
+
*/
|
|
151
|
+
__syncValue(firstSync) {
|
|
152
|
+
if (this._isMultiple) { //Check when component has slotted multi-input
|
|
153
|
+
if (!this.value && this.__checkedInput) {
|
|
154
|
+
// If component has null value and slotted input has checked value(s),
|
|
155
|
+
// assign the value of checked slotted input(s) to value property of the component.
|
|
156
|
+
this.value = this.__checkedInput;
|
|
157
|
+
} else if (this.value && !this.__checkedInput) {
|
|
158
|
+
// If component has not null value and slotted input has no value(s) checked,
|
|
159
|
+
// mark the multi-input as checked if its value(s) is part of component value property.
|
|
160
|
+
const values = this.value.toString().split(',');
|
|
161
|
+
this._slottedInputs.filter((input) => {
|
|
162
|
+
return values.includes(input.value) && !input.checked;
|
|
163
|
+
}).forEach((input) => {
|
|
164
|
+
input.checked = true;
|
|
165
|
+
if (firstSync) {
|
|
166
|
+
input.defaultChecked = true;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
} else { //When component has single-input slot
|
|
171
|
+
// eslint-disable-next-line no-lonely-if
|
|
172
|
+
if (!this.value && this._slottedInputs?.[0]?.value) {
|
|
173
|
+
// If component has null value and slotted input has value,
|
|
174
|
+
// assign the value of slotted inputs to value property of the component.
|
|
175
|
+
this.value = this._processFormChangeValue(this._slottedInputs[0].value);
|
|
176
|
+
} else if (this.value && !this._slottedInputs[0].value) {
|
|
177
|
+
// If component has not null value and slotted input has null value,
|
|
178
|
+
// assign the value of the component to value of the slotted input.
|
|
179
|
+
this._slottedInputs[0].value = this.value;
|
|
180
|
+
|
|
181
|
+
if (firstSync) {
|
|
182
|
+
this._slottedInputs[0].defaultValue = this.value;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* A method to get all slotted HTML form elements.
|
|
190
|
+
*
|
|
191
|
+
* @protected
|
|
192
|
+
* @override
|
|
193
|
+
*/
|
|
194
|
+
get _slottedInputs() {
|
|
195
|
+
const slot = this.querySelectorAll('input, textarea, select');
|
|
196
|
+
return Array.from(slot);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* A method to get slotted label element.
|
|
201
|
+
*
|
|
202
|
+
* @protected
|
|
203
|
+
* @override
|
|
204
|
+
*/
|
|
205
|
+
get _slottedLabel() {
|
|
206
|
+
return this.querySelector('label[slot="label"]');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* A method to get slotted element value.
|
|
211
|
+
*
|
|
212
|
+
* @protected
|
|
213
|
+
* @override
|
|
214
|
+
*/
|
|
215
|
+
get _slottedValue() {
|
|
216
|
+
return this._isMultiple ? this.__checkedInput : this._slottedInputs[0].value;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* A method to determine if slotted form element has multiple option.
|
|
221
|
+
*
|
|
222
|
+
* @protected
|
|
223
|
+
* @override
|
|
224
|
+
*/
|
|
225
|
+
get _isMultiple() {
|
|
226
|
+
return this._inputType === this._inputTypes.RADIO || this._inputType === this._inputTypes.CHECKBOX;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* A method to determine if slotted form element has only single option.
|
|
231
|
+
*
|
|
232
|
+
* @protected
|
|
233
|
+
* @override
|
|
234
|
+
*/
|
|
235
|
+
get _isSingle() {
|
|
236
|
+
return !(this._isMultiple || this._isSelect);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* A method to determine if slotted form element has only select option.
|
|
241
|
+
*
|
|
242
|
+
* @protected
|
|
243
|
+
* @override
|
|
244
|
+
*/
|
|
245
|
+
get _isSelect() {
|
|
246
|
+
return this._inputType === this._inputTypes.SELECT;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* A method to handle `change` event from the slotted html elements.
|
|
251
|
+
*
|
|
252
|
+
* @protected
|
|
253
|
+
* @override
|
|
254
|
+
*/
|
|
255
|
+
_onChange(changeEvent) {
|
|
256
|
+
changeEvent.stopPropagation();
|
|
257
|
+
changeEvent.preventDefault();
|
|
258
|
+
this.value = this._processFormChangeValue(this._slottedValue);
|
|
259
|
+
this._fireChangeEvent();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* A method to handle `blur` event from the slotted html elements.
|
|
264
|
+
*
|
|
265
|
+
* @protected
|
|
266
|
+
* @override
|
|
267
|
+
*/
|
|
268
|
+
_onBlur(blurEvent) {
|
|
269
|
+
blurEvent.stopPropagation();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* A method to handle `input` event from the slotted html elements.
|
|
274
|
+
*
|
|
275
|
+
* @protected
|
|
276
|
+
* @override
|
|
277
|
+
*/
|
|
278
|
+
_onInput(inputEvent) {
|
|
279
|
+
inputEvent.stopPropagation();
|
|
280
|
+
inputEvent.preventDefault();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* A method to fire the 'change' custom event from the form element.
|
|
285
|
+
*
|
|
286
|
+
* @protected
|
|
287
|
+
* @override
|
|
288
|
+
*/
|
|
289
|
+
_fireChangeEvent() {
|
|
290
|
+
this.dispatchEvent(
|
|
291
|
+
new CustomEvent('change', {
|
|
292
|
+
bubbles: true,
|
|
293
|
+
cancelable: false,
|
|
294
|
+
detail: {
|
|
295
|
+
value: this.value
|
|
296
|
+
}
|
|
297
|
+
})
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* A method to remove whitespace from the form element value.
|
|
303
|
+
*
|
|
304
|
+
* @param {string} value - Form element value to be trimmed.
|
|
305
|
+
* @returns {string} - Trimmed value.
|
|
306
|
+
* @private
|
|
307
|
+
*/
|
|
308
|
+
__removeWhitespace(value) {
|
|
309
|
+
return this._isSingle ? value.trim() : value;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* A method to process form element value before assigning to 'value' property.
|
|
314
|
+
*
|
|
315
|
+
* @param {string} value - Form elment value to be processed.
|
|
316
|
+
* @returns {string} - Processed value.
|
|
317
|
+
* @protected
|
|
318
|
+
* @override
|
|
319
|
+
*/
|
|
320
|
+
_processFormChangeValue(value) {
|
|
321
|
+
return this.__removeWhitespace(value);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* A method to get values of checked form element.
|
|
326
|
+
*
|
|
327
|
+
* @returns {string} - Array of selected values for multiple option input.
|
|
328
|
+
* @private
|
|
329
|
+
*/
|
|
330
|
+
get __checkedInput() {
|
|
331
|
+
return Array.from(this.querySelectorAll('input')).filter((input) => {
|
|
332
|
+
return input.checked;
|
|
333
|
+
}).map((input) => {
|
|
334
|
+
return input.value;
|
|
335
|
+
}).toString();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* A method to get anonymous slot template to hold html form elements.
|
|
340
|
+
*
|
|
341
|
+
* @protected
|
|
342
|
+
* @override
|
|
343
|
+
*/
|
|
344
|
+
get _addSlottedContent() {
|
|
345
|
+
return html`<slot></slot>`;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* A method to get label slot template to hold html form element label.
|
|
350
|
+
*
|
|
351
|
+
* @protected
|
|
352
|
+
* @override
|
|
353
|
+
*/
|
|
354
|
+
get _addLabel() {
|
|
355
|
+
return this.labelID.length === 0 ? html`<slot name="label"></slot>` : html``;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* A method to get heading slot template to hold html form element heading.
|
|
360
|
+
*
|
|
361
|
+
* @protected
|
|
362
|
+
* @override
|
|
363
|
+
*/
|
|
364
|
+
get _addHeading() {
|
|
365
|
+
return html`<span class="input-heading">${this.heading}</span>`;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* A method to get standard template for type `standard`.
|
|
370
|
+
*
|
|
371
|
+
* @protected
|
|
372
|
+
* @override
|
|
373
|
+
*/
|
|
374
|
+
get standardTemplate() {
|
|
375
|
+
return this._addSlottedContent;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { dedupeMixin } from '@muonic/muon';
|
|
2
|
+
|
|
3
|
+
export const ImageHolderMixin = dedupeMixin((superClass) =>
|
|
4
|
+
class ImageHolderdMixinClass extends superClass {
|
|
5
|
+
|
|
6
|
+
static get properties() {
|
|
7
|
+
return {
|
|
8
|
+
image: {
|
|
9
|
+
type: String
|
|
10
|
+
},
|
|
11
|
+
alt: {
|
|
12
|
+
type: String
|
|
13
|
+
},
|
|
14
|
+
background: {
|
|
15
|
+
type: Boolean
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
);
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { html, ifDefined, dedupeMixin } from '@muonic/muon';
|
|
2
|
+
import { FormElementMixin } from '@muon/mixins/form-element-mixin';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A mixin to enable mask and separator features to a form element.
|
|
6
|
+
* `mask` property is supported for input of type text, date, tel.
|
|
7
|
+
* `separator` property is supported for input of type text, date, tel.
|
|
8
|
+
*
|
|
9
|
+
* @mixin
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export const MaskMixin = dedupeMixin((superclass) =>
|
|
13
|
+
class MaskMixinClass extends FormElementMixin(superclass) {
|
|
14
|
+
static get properties() {
|
|
15
|
+
return {
|
|
16
|
+
mask: {
|
|
17
|
+
type: String
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
separator: {
|
|
21
|
+
type: String
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
constructor() {
|
|
27
|
+
super();
|
|
28
|
+
|
|
29
|
+
this.mask = '';
|
|
30
|
+
this.separator = '';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
firstUpdated() {
|
|
34
|
+
super.firstUpdated();
|
|
35
|
+
|
|
36
|
+
if (this.mask) {
|
|
37
|
+
this._slottedInputs.map((input) => {
|
|
38
|
+
input.setAttribute('maxlength', this.mask.length);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A method to process the form element value in response to `input` event.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} value - Value to be processed with mask and separator.
|
|
47
|
+
* @returns {string} - Processed value.
|
|
48
|
+
* @protected
|
|
49
|
+
*/
|
|
50
|
+
_processMaskInputValue(value) {
|
|
51
|
+
if (!this._isSingle) {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
const inputElement = this._slottedInputs[0];
|
|
55
|
+
if (ifDefined(this.separator)) {
|
|
56
|
+
value = this.updateValueAndCursor(inputElement);
|
|
57
|
+
} else {
|
|
58
|
+
value = inputElement.value;
|
|
59
|
+
}
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A method to process the form element value in response to `change` event.
|
|
65
|
+
*
|
|
66
|
+
* @param {string} value - Value to be processed with mask and separator.
|
|
67
|
+
* @returns {string} - Processed value.
|
|
68
|
+
* @protected
|
|
69
|
+
*/
|
|
70
|
+
_processMaskChangeValue(value) {
|
|
71
|
+
if (ifDefined(this.separator)) {
|
|
72
|
+
value = this.formatWithMaskAndSeparator(value);
|
|
73
|
+
this._slottedInputs[0].value = value;
|
|
74
|
+
}
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A method to update the form element value with separator in adjusted indices and cursor position.
|
|
80
|
+
*
|
|
81
|
+
* @param {HTMLElement} input - HTMLInputElement value to be updated with seperators.
|
|
82
|
+
* @returns {string} - Updated form element value.
|
|
83
|
+
*/
|
|
84
|
+
updateValueAndCursor(input) {
|
|
85
|
+
let value = input.value;
|
|
86
|
+
let cursor = input.selectionStart;
|
|
87
|
+
const diff = this.value.length - value.length;
|
|
88
|
+
|
|
89
|
+
if (diff > 0 && this.mask.charAt(cursor) === this.separator) {
|
|
90
|
+
value = value.slice(0, cursor - 1) + (cursor < value.length ? value.slice(cursor) : '');
|
|
91
|
+
cursor -= 1;
|
|
92
|
+
}
|
|
93
|
+
const formattedValue = this.formatWithMaskAndSeparator(value);
|
|
94
|
+
input.value = formattedValue;
|
|
95
|
+
|
|
96
|
+
if (this.mask.charAt(cursor) === this.separator) {
|
|
97
|
+
cursor += 1;
|
|
98
|
+
}
|
|
99
|
+
this.updateComplete.then(() => {
|
|
100
|
+
input.setSelectionRange(cursor, cursor);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return formattedValue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* A method to format the form element value with separator adjusted to correct indices
|
|
108
|
+
* after editing the form element value.
|
|
109
|
+
*
|
|
110
|
+
* @param {string} value - Value of the form element.
|
|
111
|
+
* @returns {string} - Value with adjusted separator in correct indices.
|
|
112
|
+
*/
|
|
113
|
+
formatWithMaskAndSeparator(value) {
|
|
114
|
+
const formattedValue = this.__formatInputWithoutSeparator(value);
|
|
115
|
+
const parts = this.mask.split(this.separator);
|
|
116
|
+
let processedValue = '';
|
|
117
|
+
let length = 0;
|
|
118
|
+
let currentLength = 0;
|
|
119
|
+
|
|
120
|
+
for (let i = 0; i < parts.length && length < formattedValue.length; i++) {
|
|
121
|
+
const remainingLength = formattedValue.length - length;
|
|
122
|
+
const splitPoint = remainingLength > parts[i].length ? parts[i].length : remainingLength;
|
|
123
|
+
|
|
124
|
+
processedValue += formattedValue.substr(length, splitPoint);
|
|
125
|
+
currentLength += parts[i].length;
|
|
126
|
+
|
|
127
|
+
if (i < (parts.length - 1) && processedValue.length === currentLength) {
|
|
128
|
+
processedValue += this.separator;
|
|
129
|
+
currentLength += 1;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
length += parts[i].length;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return processedValue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* A method to remove separator from the value of the form element.
|
|
140
|
+
*
|
|
141
|
+
* @param {string} value - Form element value.
|
|
142
|
+
* @returns {string} - Value with separator removed.
|
|
143
|
+
*/
|
|
144
|
+
__formatInputWithoutSeparator(value) {
|
|
145
|
+
return value.split(this.separator).join('');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
get _addMask() {
|
|
149
|
+
if (this.mask && this._isSingle) {
|
|
150
|
+
const length = this.value ? this.value.length : 0;
|
|
151
|
+
let updatedMask = new Array(length + 1).join(' ');
|
|
152
|
+
updatedMask += this.mask.slice(length);
|
|
153
|
+
return html`<div aria-hidden="true" class="input-mask">${updatedMask}</div>`;
|
|
154
|
+
} else {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
);
|