@lion/ui 0.0.3 → 0.0.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.
- package/components/core/src/SlotMixin.js +180 -42
- package/components/core/test/SlotMixin.test.js +341 -138
- package/components/core/types/SlotMixinTypes.ts +12 -1
- package/components/input-tel/src/LionInputTel.js +0 -9
- package/components/input-tel-dropdown/src/LionInputTelDropdown.js +32 -29
- package/components/input-tel-dropdown/test-suites/LionInputTelDropdown.suite.js +5 -6
- package/components/localize/types/LocalizeMixinTypes.ts +1 -1
- package/custom-elements.json +1435 -133
- package/dist-types/components/calendar/src/LionCalendar.d.ts +1 -37
- package/dist-types/components/calendar/src/LionCalendar.d.ts.map +1 -1
- package/dist-types/components/core/src/SlotMixin.d.ts +2 -0
- package/dist-types/components/core/src/SlotMixin.d.ts.map +1 -1
- package/dist-types/components/core/test/SlotMixin.test.d.ts +1 -1
- package/dist-types/components/core/test/SlotMixin.test.d.ts.map +1 -1
- package/dist-types/components/core/types/SlotMixinTypes.d.ts +9 -1
- package/dist-types/components/core/types/SlotMixinTypes.d.ts.map +1 -1
- package/dist-types/components/input-amount/src/LionInputAmount.d.ts +1 -37
- package/dist-types/components/input-amount/src/LionInputAmount.d.ts.map +1 -1
- package/dist-types/components/input-date/src/LionInputDate.d.ts +1 -37
- package/dist-types/components/input-date/src/LionInputDate.d.ts.map +1 -1
- package/dist-types/components/input-datepicker/src/LionCalendarOverlayFrame.d.ts +1 -37
- package/dist-types/components/input-datepicker/src/LionCalendarOverlayFrame.d.ts.map +1 -1
- package/dist-types/components/input-email/src/LionInputEmail.d.ts +1 -37
- package/dist-types/components/input-email/src/LionInputEmail.d.ts.map +1 -1
- package/dist-types/components/input-iban/src/LionInputIban.d.ts +1 -37
- package/dist-types/components/input-iban/src/LionInputIban.d.ts.map +1 -1
- package/dist-types/components/input-tel/src/LionInputTel.d.ts +1 -42
- package/dist-types/components/input-tel/src/LionInputTel.d.ts.map +1 -1
- package/dist-types/components/input-tel-dropdown/src/LionInputTelDropdown.d.ts +14 -8
- package/dist-types/components/input-tel-dropdown/src/LionInputTelDropdown.d.ts.map +1 -1
- package/dist-types/components/input-tel-dropdown/test-suites/LionInputTelDropdown.suite.d.ts.map +1 -1
- package/dist-types/components/localize/src/LocalizeMixin.d.ts +1 -37
- package/dist-types/components/localize/src/LocalizeMixin.d.ts.map +1 -1
- package/dist-types/components/localize/types/LocalizeMixinTypes.d.ts +1 -1
- package/dist-types/components/localize/types/LocalizeMixinTypes.d.ts.map +1 -1
- package/dist-types/components/pagination/src/LionPagination.d.ts +1 -37
- package/dist-types/components/pagination/src/LionPagination.d.ts.map +1 -1
- package/dist-types/components/progress-indicator/src/LionProgressIndicator.d.ts +1 -37
- package/dist-types/components/progress-indicator/src/LionProgressIndicator.d.ts.map +1 -1
- package/dist-types/exports/core.d.ts +1 -0
- package/dist-types/exports/core.d.ts.map +1 -1
- package/dist-types/exports/form-core.d.ts +2 -0
- package/dist-types/exports/form-core.d.ts.map +1 -1
- package/dist-types/exports/input-tel-dropdown.d.ts +2 -0
- package/dist-types/exports/input-tel-dropdown.d.ts.map +1 -1
- package/dist-types/exports/input-tel.d.ts +1 -0
- package/dist-types/exports/input-tel.d.ts.map +1 -1
- package/dist-types/exports/localize.d.ts +3 -0
- package/dist-types/exports/localize.d.ts.map +1 -1
- package/dist-types/exports/overlays.d.ts +4 -0
- package/dist-types/exports/overlays.d.ts.map +1 -1
- package/dist-types/tsconfig.tsbuildinfo +1 -1
- package/docs/components/checkbox-group/overview.md +2 -2
- package/docs/components/collapsible/overview.md +1 -1
- package/docs/components/combobox/overview.md +1 -1
- package/docs/components/combobox/use-cases.md +1 -1
- package/docs/components/dialog/overview.md +1 -1
- package/docs/components/fieldset/overview.md +5 -5
- package/docs/components/form/overview.md +3 -3
- package/docs/components/icon/use-cases.md +1 -1
- package/docs/components/input/overview.md +1 -1
- package/docs/components/input-amount/overview.md +3 -3
- package/docs/components/input-date/overview.md +3 -3
- package/docs/components/input-datepicker/overview.md +5 -5
- package/docs/components/input-email/overview.md +2 -2
- package/docs/components/input-iban/overview.md +2 -2
- package/docs/components/input-range/overview.md +2 -2
- package/docs/components/input-stepper/overview.md +1 -1
- package/docs/components/input-tel/overview.md +1 -1
- package/docs/components/input-tel/use-cases.md +6 -6
- package/docs/components/input-tel-dropdown/overview.md +1 -1
- package/docs/components/radio-group/overview.md +3 -3
- package/docs/components/select-rich/use-cases.md +1 -1
- package/docs/components/tabs/examples.md +1 -1
- package/docs/fundamentals/index.md +4 -4
- package/docs/fundamentals/node-tools/providence-analytics/LocalConfiguration.md +1 -1
- package/docs/fundamentals/node-tools/providence-analytics/QueryResult.md +1 -1
- package/docs/fundamentals/node-tools/providence-analytics/analyzer.md +1 -1
- package/docs/fundamentals/node-tools/providence-analytics/dashboard.md +1 -1
- package/docs/fundamentals/node-tools/providence-analytics/overview.md +3 -3
- package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
- package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
- package/docs/fundamentals/systems/core/overview.md +1 -1
- package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
- package/docs/fundamentals/systems/form/model-value.md +1 -1
- package/docs/fundamentals/systems/form/overview.md +28 -28
- package/docs/fundamentals/systems/form/validate.md +1 -1
- package/docs/fundamentals/systems/localize/overview.md +4 -4
- package/docs/fundamentals/systems/overlays/configuration.md +2 -2
- package/docs/fundamentals/systems/overlays/overview.md +5 -5
- package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
- package/docs/fundamentals/tools/helpers/overview.md +1 -1
- package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
- package/docs/guides/how-to/create-a-custom-field.md +5 -5
- package/docs/guides/how-to/extend-a-native-input.md +2 -2
- package/docs/guides/index.md +8 -8
- package/docs/guides/principles/index.md +5 -5
- package/docs/guides/principles/styling.md +1 -1
- package/exports/core.js +2 -0
- package/exports/form-core.js +3 -0
- package/exports/input-tel-dropdown.js +3 -0
- package/exports/input-tel.js +2 -0
- package/exports/localize.js +4 -0
- package/exports/overlays.js +5 -0
- package/package.json +3 -2
|
@@ -6,10 +6,22 @@ import { isTemplateResult } from 'lit/directive-helpers.js';
|
|
|
6
6
|
/**
|
|
7
7
|
* @typedef {import('../types/SlotMixinTypes.js').SlotMixin} SlotMixin
|
|
8
8
|
* @typedef {import('../types/SlotMixinTypes.js').SlotsMap} SlotsMap
|
|
9
|
+
* @typedef {import('../types/SlotMixinTypes.js').SlotFunctionResult} SlotFunctionResult
|
|
10
|
+
* @typedef {import('../types/SlotMixinTypes.js').SlotRerenderObject} SlotRerenderObject
|
|
9
11
|
* @typedef {import('lit').LitElement} LitElement
|
|
10
12
|
*/
|
|
11
13
|
|
|
14
|
+
const isRerenderConfig = (/** @type {SlotFunctionResult} */ o) =>
|
|
15
|
+
!Array.isArray(o) && typeof o === 'object' && 'template' in o;
|
|
16
|
+
|
|
12
17
|
/**
|
|
18
|
+
* All intricacies involved in managing light dom can be delegated to SlotMixin. Amongst others, it automatically:
|
|
19
|
+
* - mediates between light dom provided by the user ('public slots') and light dom provided by the component author ('private slots').
|
|
20
|
+
* - allows to hook into the reactive update loop of LitElement (rerendering on property changes)
|
|
21
|
+
* - respects the scoped registry belonging to the shadow root.
|
|
22
|
+
*
|
|
23
|
+
* Be sure to read all details about SlotMixin in the [SlotMixin documentation](docs/fundamentals/systems/core/SlotMixin.md)
|
|
24
|
+
*
|
|
13
25
|
* @type {SlotMixin}
|
|
14
26
|
* @param {import('@open-wc/dedupe-mixin').Constructor<LitElement>} superclass
|
|
15
27
|
*/
|
|
@@ -25,8 +37,35 @@ const SlotMixinImplementation = superclass =>
|
|
|
25
37
|
|
|
26
38
|
constructor() {
|
|
27
39
|
super();
|
|
28
|
-
|
|
29
|
-
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The roots that are used to do a rerender in.
|
|
43
|
+
* In case of a SlotRerenderObject, this will create a render wrapper in the scoped context (shadow root)
|
|
44
|
+
* and connect this under the slot name to the light dom. All (re)renders will happen from here,
|
|
45
|
+
* so that all interactions work as intended and no focus issues can arise (which would be the case
|
|
46
|
+
* when (cloned) nodes of a render outcome would be moved around)
|
|
47
|
+
* @private
|
|
48
|
+
* @type { Map<string, HTMLDivElement> } */
|
|
49
|
+
this.__scopedRenderRoots = new Map();
|
|
50
|
+
/**
|
|
51
|
+
* @private
|
|
52
|
+
* @type {Set<string>}
|
|
53
|
+
*/
|
|
54
|
+
this.__slotsThatNeedRerender = new Set();
|
|
55
|
+
/**
|
|
56
|
+
* Those are slots that should not be touched by SlotMixin
|
|
57
|
+
* @private
|
|
58
|
+
* @type {Set<string>}
|
|
59
|
+
*/
|
|
60
|
+
this.__slotsProvidedByUserOnFirstConnected = new Set();
|
|
61
|
+
/**
|
|
62
|
+
* Those are slots that should be touched by SlotMixin
|
|
63
|
+
* The opposite of __slotsProvidedByUserOnFirstConnected,
|
|
64
|
+
* also taking into account undefined (a.k.a. conditional) slots
|
|
65
|
+
* @private
|
|
66
|
+
* @type {Set<string>}
|
|
67
|
+
*/
|
|
68
|
+
this.__privateSlots = new Set();
|
|
30
69
|
}
|
|
31
70
|
|
|
32
71
|
connectedCallback() {
|
|
@@ -34,61 +73,160 @@ const SlotMixinImplementation = superclass =>
|
|
|
34
73
|
this._connectSlotMixin();
|
|
35
74
|
}
|
|
36
75
|
|
|
76
|
+
/**
|
|
77
|
+
* @param {string} slotName
|
|
78
|
+
*/
|
|
79
|
+
__rerenderSlot(slotName) {
|
|
80
|
+
const slotFunctionResult = /** @type {SlotRerenderObject} */ (this.slots[slotName]());
|
|
81
|
+
this.__renderTemplateInScopedContext({
|
|
82
|
+
template: slotFunctionResult.template,
|
|
83
|
+
slotName,
|
|
84
|
+
shouldRerender: true,
|
|
85
|
+
});
|
|
86
|
+
slotFunctionResult.afterRender?.();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Here we rerender slots defined with a `SlotRerenderObject`
|
|
91
|
+
* @param {import('lit-element').PropertyValues } changedProperties
|
|
92
|
+
*/
|
|
93
|
+
updated(changedProperties) {
|
|
94
|
+
super.updated(changedProperties);
|
|
95
|
+
|
|
96
|
+
if (this.__slotsThatNeedRerender.size) {
|
|
97
|
+
for (const slotName of Array.from(this.__slotsThatNeedRerender)) {
|
|
98
|
+
this.__rerenderSlot(slotName);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (this.__isFirstSlotUpdate) {
|
|
103
|
+
this.__isFirstSlotUpdate = false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
37
107
|
/**
|
|
38
108
|
* @private
|
|
39
|
-
* @param {
|
|
109
|
+
* @param {object} opts
|
|
110
|
+
* @param {import('lit').TemplateResult} opts.template
|
|
111
|
+
* @param {string} opts.slotName
|
|
112
|
+
* @param {boolean} [opts.shouldRerender] false when TemplateResult, true when SlotRerenderObject
|
|
40
113
|
*/
|
|
41
|
-
|
|
114
|
+
__renderTemplateInScopedContext({ template, slotName, shouldRerender }) {
|
|
42
115
|
// @ts-expect-error wait for browser support
|
|
43
116
|
const supportsScopedRegistry = !!ShadowRoot.prototype.createElement;
|
|
44
117
|
const registryRoot = supportsScopedRegistry ? this.shadowRoot : document;
|
|
45
|
-
|
|
46
|
-
|
|
118
|
+
|
|
119
|
+
let renderTarget;
|
|
120
|
+
// Reuse the existing offline renderTargets for results consistent with that of rendering to one target (shadow dom)
|
|
121
|
+
if (this.__scopedRenderRoots.has(slotName)) {
|
|
122
|
+
renderTarget = this.__scopedRenderRoots.get(slotName);
|
|
123
|
+
} else {
|
|
124
|
+
// @ts-expect-error wait for browser support
|
|
125
|
+
renderTarget = registryRoot.createElement('div');
|
|
126
|
+
if (shouldRerender) {
|
|
127
|
+
renderTarget.slot = slotName;
|
|
128
|
+
this.appendChild(renderTarget);
|
|
129
|
+
}
|
|
130
|
+
this.__scopedRenderRoots.set(slotName, renderTarget);
|
|
131
|
+
}
|
|
132
|
+
|
|
47
133
|
// Providing all options breaks Safari; keep host and creationScope
|
|
48
134
|
const { creationScope, host } = this.renderOptions;
|
|
49
|
-
render(template,
|
|
50
|
-
|
|
135
|
+
render(template, renderTarget, { creationScope, host });
|
|
136
|
+
|
|
137
|
+
return renderTarget;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @param {object} options
|
|
142
|
+
* @param {Node[]} options.nodes
|
|
143
|
+
* @param {Element} [options.renderParent] It's recommended to create a render target in light dom (like <div slot=myslot>),
|
|
144
|
+
* which can be used as a render target for most
|
|
145
|
+
* @param {string} options.slotName For the first render, it's best to use slotName
|
|
146
|
+
*/
|
|
147
|
+
__appendNodesForOneTimeRender({ nodes, renderParent = this, slotName }) {
|
|
148
|
+
for (const node of nodes) {
|
|
149
|
+
if (!(node instanceof Node)) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
// Here, we add the slot name to the node that is an element
|
|
153
|
+
// (ignoring helper comment nodes that might be in our nodes array)
|
|
154
|
+
if (node instanceof Element && slotName && slotName !== '') {
|
|
155
|
+
node.setAttribute('slot', slotName);
|
|
156
|
+
}
|
|
157
|
+
renderParent.appendChild(node);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Here we look what's inside our `get slots`.
|
|
163
|
+
* Rerenderable slots get scheduled and "one time slots" get rendered once on connected
|
|
164
|
+
* @param {string[]} slotNames
|
|
165
|
+
*/
|
|
166
|
+
__initSlots(slotNames) {
|
|
167
|
+
for (const slotName of slotNames) {
|
|
168
|
+
if (this.__slotsProvidedByUserOnFirstConnected.has(slotName)) {
|
|
169
|
+
// eslint-disable-next-line no-continue
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const slotFunctionResult = this.slots[slotName]();
|
|
174
|
+
// Allow to conditionally return a slot
|
|
175
|
+
if (slotFunctionResult === undefined) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!this.__isConnectedSlotMixin) {
|
|
180
|
+
this.__privateSlots.add(slotName);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (isTemplateResult(slotFunctionResult)) {
|
|
184
|
+
const renderTarget = this.__renderTemplateInScopedContext({
|
|
185
|
+
template: slotFunctionResult,
|
|
186
|
+
slotName,
|
|
187
|
+
});
|
|
188
|
+
const nodes = Array.from(renderTarget.childNodes);
|
|
189
|
+
this.__appendNodesForOneTimeRender({ nodes, renderParent: this, slotName });
|
|
190
|
+
} else if (slotFunctionResult instanceof Node) {
|
|
191
|
+
const nodes = [/** @type {Node} */ (slotFunctionResult)];
|
|
192
|
+
this.__appendNodesForOneTimeRender({ nodes, renderParent: this, slotName });
|
|
193
|
+
} else if (isRerenderConfig(slotFunctionResult)) {
|
|
194
|
+
// Rerenderable slots are scheduled in the "updated loop"
|
|
195
|
+
this.__slotsThatNeedRerender.add(slotName);
|
|
196
|
+
} else {
|
|
197
|
+
throw new Error(
|
|
198
|
+
'Please provide a function inside "get slots()" returning TemplateResult | Node | {template:TemplateResult, afterRender?:function} | undefined',
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
51
202
|
}
|
|
52
203
|
|
|
53
204
|
/**
|
|
54
205
|
* @protected
|
|
55
206
|
*/
|
|
56
207
|
_connectSlotMixin() {
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
const hasSlottableFromUser =
|
|
60
|
-
slotName === ''
|
|
61
|
-
? // for default slot (''), we can't use el.slot because polyfill for IE11
|
|
62
|
-
// will do .querySelector('[slot=]') which produces a fatal error
|
|
63
|
-
// therefore we check if there's children that do not have a slot attr
|
|
64
|
-
Array.from(this.children).find(el => !el.hasAttribute('slot'))
|
|
65
|
-
: Array.from(this.children).find(el => el.slot === slotName);
|
|
66
|
-
|
|
67
|
-
if (!hasSlottableFromUser) {
|
|
68
|
-
const slotContent = this.slots[slotName]();
|
|
69
|
-
/** @type {Node[]} */
|
|
70
|
-
let nodes = [];
|
|
71
|
-
|
|
72
|
-
if (isTemplateResult(slotContent)) {
|
|
73
|
-
nodes = this.__renderAsNodes(slotContent);
|
|
74
|
-
} else if (!Array.isArray(slotContent)) {
|
|
75
|
-
nodes = [/** @type {Node} */ (slotContent)];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
nodes.forEach(node => {
|
|
79
|
-
if (!(node instanceof Node)) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
if (node instanceof Element && slotName !== '') {
|
|
83
|
-
node.setAttribute('slot', slotName);
|
|
84
|
-
}
|
|
85
|
-
this.appendChild(node);
|
|
86
|
-
this.__privateSlots.add(slotName);
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
this.__isConnectedSlotMixin = true;
|
|
208
|
+
if (this.__isConnectedSlotMixin) {
|
|
209
|
+
return;
|
|
91
210
|
}
|
|
211
|
+
|
|
212
|
+
const allSlots = Object.keys(this.slots);
|
|
213
|
+
|
|
214
|
+
for (const slotName of allSlots) {
|
|
215
|
+
const hasSlottableFromUser =
|
|
216
|
+
slotName === ''
|
|
217
|
+
? // for default slot (''), we can't use el.slot because polyfill for IE11
|
|
218
|
+
// will do .querySelector('[slot=]') which produces a fatal error
|
|
219
|
+
// therefore we check if there's children that do not have a slot attr
|
|
220
|
+
Array.from(this.children).find(el => !el.hasAttribute('slot'))
|
|
221
|
+
: Array.from(this.children).find(el => el.slot === slotName);
|
|
222
|
+
|
|
223
|
+
if (hasSlottableFromUser) {
|
|
224
|
+
this.__slotsProvidedByUserOnFirstConnected.add(slotName);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
this.__initSlots(allSlots);
|
|
229
|
+
this.__isConnectedSlotMixin = true;
|
|
92
230
|
}
|
|
93
231
|
|
|
94
232
|
/**
|