@shortfuse/materialdesignweb 0.7.1 → 0.7.4

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.
@@ -4,10 +4,10 @@
4
4
 
5
5
  /** @typedef {import('../core/CustomElement.js').default} CustomElement */
6
6
 
7
- const DOMString = { nullParser: String };
8
-
9
7
  const FORM_IPC_EVENT = 'mdw-form-associated-changed';
10
8
 
9
+ const DOMString = { nullParser: String, value: '' };
10
+
11
11
  /**
12
12
  * @param {ReturnType<import('./StateMixin.js').default>} Base
13
13
  */
@@ -18,7 +18,6 @@ export default function FormAssociatedMixin(Base) {
18
18
  formAssociated: true,
19
19
  })
20
20
  .set({
21
- _valueDirty: false,
22
21
  /** @type {EventListener} */
23
22
  _ipcListener: null,
24
23
  /** @type {EventTarget} */
@@ -29,44 +28,116 @@ export default function FormAssociatedMixin(Base) {
29
28
  autocomplete: DOMString,
30
29
  name: DOMString,
31
30
  readOnly: { attr: 'readonly', type: 'boolean' },
31
+ defaultChecked: { attr: 'checked', type: 'boolean' },
32
+ _checkedDirty: 'boolean',
33
+ /* "Checkedness" */
34
+ _checked: 'boolean',
32
35
  required: 'boolean',
33
36
  type: DOMString,
34
37
  // [CEReactions] attribute [LegacyNullToEmptyString] DOMString value;
35
- _value: {
36
- empty: '',
37
- /**
38
- * @param {string} oldValue
39
- * @param {string} newValue
40
- */
41
- changedCallback(oldValue, newValue) {
42
- this.propChangedCallback('value', oldValue, newValue);
43
- },
44
- },
38
+ _defaultValue: { reflect: true, attr: 'value' },
39
+ _value: { empty: '' },
40
+ _valueDirty: 'boolean',
45
41
  _invalid: 'boolean',
46
42
  _badInput: 'boolean',
47
43
  _validationMessage: 'string',
48
44
  _formDisabled: 'boolean',
45
+ _formReset: 'boolean',
49
46
  })
50
47
  .observe({
51
48
  erroredState({ _invalid }) { return _invalid; },
49
+ defaultValue: {
50
+ reflect: false,
51
+ get({ _defaultValue }) {
52
+ return _defaultValue ?? '';
53
+ },
54
+ set(value) {
55
+ this._defaultValue = String(value);
56
+ },
57
+ },
58
+ _valueBehavior({ type }) {
59
+ switch (type) {
60
+ case 'radio':
61
+ case 'checkbox':
62
+ return 'default/on';
63
+ case 'hidden':
64
+ case 'button':
65
+ case 'submit':
66
+ case 'image':
67
+ case 'reset':
68
+ return 'default';
69
+ case 'file': return 'filename';
70
+ default: return 'value';
71
+ }
72
+ },
52
73
  })
53
- .define({
54
- form() { return this.elementInternals.form; },
55
- validity() { return this.elementInternals.validity; },
56
- validationMessage() { return this.elementInternals.validationMessage; },
57
- willValidate() { return this.elementInternals.willValidate; },
58
- labels() { return this.elementInternals.labels; },
74
+ .methods({
75
+ /**
76
+ * Default behavior can should likely be overridden
77
+ * @param {string} value
78
+ */
79
+ _onSetValue(value) {
80
+ this._value = value;
81
+ },
82
+ /**
83
+ * Default behavior can should likely be overridden
84
+ * @param {boolean} checked
85
+ */
86
+ _onSetChecked(checked) {
87
+ this._checked = checked;
88
+ },
89
+ })
90
+ .observe({
59
91
  value: {
60
- get() {
61
- return this._value;
92
+ reflect: false,
93
+ get({ _valueBehavior, _defaultValue, _value }) {
94
+ switch (_valueBehavior) {
95
+ default:
96
+ return _value;
97
+ case 'default':
98
+ return _defaultValue ?? '';
99
+ case 'default/on':
100
+ return _defaultValue ?? 'on';
101
+ case 'filename':
102
+ throw new Error('Not supported!');
103
+ }
62
104
  },
63
105
  /** @param {string} v */
64
106
  set(v) {
65
- this._valueDirty = true;
66
- this._value = v;
107
+ switch (this._valueBehavior) {
108
+ case 'value':
109
+ this._valueDirty = true;
110
+ this._onSetValue(v);
111
+ break;
112
+ default:
113
+ this.defaultValue = v;
114
+ }
115
+ },
116
+ },
117
+ /**
118
+ * Part of FormAssociatedMixin for simplicity.
119
+ * Enumerability doesn't guarantee checked state will be passed or used.
120
+ */
121
+ checked: {
122
+ reflect: false,
123
+ type: 'boolean',
124
+ get({ _checked }) {
125
+ return _checked;
126
+ },
127
+ /** @param {boolean} checked */
128
+ set(checked) {
129
+ this._checkedDirty = true;
130
+ this._onSetChecked(checked);
67
131
  },
68
132
  },
69
133
  })
134
+ .define({
135
+ form() { return this.elementInternals.form; },
136
+ validity() { return this.elementInternals.validity; },
137
+ validationMessage() { return this.elementInternals.validationMessage; },
138
+ willValidate() { return this.elementInternals.willValidate; },
139
+ labels() { return this.elementInternals.labels; },
140
+ })
70
141
  .observe({
71
142
  disabledState({ _formDisabled, disabled }) {
72
143
  if (_formDisabled) return true;
@@ -124,16 +195,31 @@ export default function FormAssociatedMixin(Base) {
124
195
  */
125
196
  formAssociatedCallback(form) {
126
197
  this.refreshFormAssociation();
198
+ console.debug('FormAssociatedMixin: formAssociatedCallback', this);
199
+ // Set value on association (not done by default?)
127
200
  },
128
201
 
129
202
  /**
130
203
  * @param {CustomEvent<[string, string]>} event
131
204
  * @return {void}
132
205
  */
133
-
134
206
  formIPCEvent(event) {
135
- console.warn('Virtual formIPCEvent invoked.');
136
- // virtual
207
+ if (event.target instanceof HTMLFormElement && event.target !== this.form) {
208
+ console.warn('Control.formIPCEvent: Abort from wrong form');
209
+ return;
210
+ }
211
+ if (this.type !== 'radio') {
212
+ console.warn('Control.formIPCEvent: Abort from not radio');
213
+ return;
214
+ }
215
+ const [name, value] = event.detail;
216
+ if (this.name !== name) return;
217
+ if (value === this.value) {
218
+ // console.log('Control.formIPCEvent: Continue match', this.name, this.value);
219
+ } else {
220
+ console.debug('FormAssociatedMixin: Unchecking', this);
221
+ this.checked = false;
222
+ }
137
223
  },
138
224
 
139
225
  /** @param {boolean} disabled */
@@ -142,8 +228,10 @@ export default function FormAssociatedMixin(Base) {
142
228
  },
143
229
 
144
230
  formResetCallback() {
231
+ this._formReset = true; // Fires Change Event
145
232
  this._valueDirty = false;
146
233
  this.checkValidity();
234
+ this._formReset = false;
147
235
  },
148
236
 
149
237
  /**
@@ -151,10 +239,52 @@ export default function FormAssociatedMixin(Base) {
151
239
  * @param {'autocomplete'|'restore'} mode
152
240
  */
153
241
  formStateRestoreCallback(state, mode) {
154
- if (typeof state === 'string') {
155
- this.value = state;
156
- } else {
157
- console.warn('Could not restore', state);
242
+ if (navigator.userAgent.includes('Edg/')) {
243
+ console.warn('Chromium Bug: 1429585 - Ignoring formStateRestoreCallback on Edge', { state, mode });
244
+ // formStateRestoreCallback is broken on Edge
245
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=1429585
246
+ return;
247
+ }
248
+ if (typeof state !== 'string') {
249
+ console.warn('FormAssociatedMixin: (Restore) Could not restore', state);
250
+ return;
251
+ }
252
+ if (this.type === 'checkbox' || this.type === 'radio') {
253
+ console.debug('FormAssociatedMixin: (Restore) Setting Checkbox checked state.', state, this);
254
+ this.checked = (state === 'checked');
255
+ return;
256
+ }
257
+ if (this.type === 'radio') {
258
+ // Due to lifecycle quirks, other radio elements on the page may not have
259
+ // been upgraded to Custom Element yet and would not receive
260
+ // the 'uncheck' communication. Delay notice until then.
261
+ this.checked = (state === 'checked');
262
+ return;
263
+ }
264
+
265
+ console.debug('FormAssociatedMixin: (Restore) Setting value state.', state, this);
266
+ this.value = state;
267
+ },
268
+
269
+ _updateFormAssociatedValue() {
270
+ switch (this.type) {
271
+ case 'radio':
272
+ if (this.checked) {
273
+ this._notifyRadioChange(this.name, this.value || 'on');
274
+ }
275
+ // Fallthrough
276
+ case 'checkbox':
277
+ if (this.checked) {
278
+ console.log('FormAssociatedMixin: setFormValue', this.name, `(${this.value}, 'checked')`, this);
279
+ this.elementInternals.setFormValue(this.value, 'checked');
280
+ } else {
281
+ console.log('FormAssociatedMixin: setFormValue', this.name, "(null, 'unchecked')", this);
282
+ this.elementInternals.setFormValue(null, 'unchecked');
283
+ }
284
+ break;
285
+ default:
286
+ console.log('FormAssociatedMixin: updating form value', this.name, this.value);
287
+ this.elementInternals.setFormValue(this.value);
158
288
  }
159
289
  },
160
290
  })
@@ -166,5 +296,11 @@ export default function FormAssociatedMixin(Base) {
166
296
  // Bind to global if no form is present (used by radio)
167
297
  this.refreshFormAssociation();
168
298
  },
299
+ checkedChanged() {
300
+ this._updateFormAssociatedValue();
301
+ },
302
+ valueChanged() {
303
+ this._updateFormAssociatedValue();
304
+ },
169
305
  });
170
306
  }