@claspo/components 1.6.2 → 1.7.0-components-build-perf
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/package.json +6 -4
- package/script/SysButtonComponent/SysButton.manifest.js +126 -0
- package/script/SysButtonComponent/SysButton.styles.js +64 -0
- package/script/SysButtonComponent/SysButtonComponent.js +231 -0
- package/script/SysColumnComponent/SysColumn.manifest.js +17 -0
- package/script/SysColumnComponent/SysColumnComponent.js +107 -0
- package/script/SysColumnsComponent/SysColumns.manifest.js +17 -0
- package/script/SysColumnsComponent/SysColumnsComponent.js +53 -0
- package/script/SysColumnsComponent/getStyleElement.js +23 -0
- package/script/SysContainerComponent/SysBaseContainerComponent.js +41 -0
- package/script/SysContainerComponent/SysContainer.manifest.js +18 -0
- package/script/SysContainerComponent/SysContainerComponent.js +86 -0
- package/script/SysImageComponent/SysImage.manifest.js +18 -0
- package/script/SysImageComponent/SysImageComponent.js +378 -0
- package/script/SysImageComponent/getStyleElement.js +18 -0
- package/script/SysInputComponent/EmailSuggesting.js +252 -0
- package/script/SysInputComponent/InputFormControl.js +136 -0
- package/script/SysInputComponent/SysInput.manifest.js +728 -0
- package/script/SysInputComponent/SysInputComponent.js +84 -0
- package/script/SysInputComponent/emailProvidersList.js +158 -0
- package/script/SysInputComponent/getOverlayStyles.js +220 -0
- package/script/SysInputComponent/getStyleElement.js +69 -0
- package/script/SysInputComponent/inputValidators.js +293 -0
- package/script/SysTextComponent/SysText.manifest.js +29 -0
- package/script/SysTextComponent/SysTextComponent.js +147 -0
- package/script/SysTextComponent/TextRoller.js +298 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { flat } from '@claspo/common/flat';
|
|
2
|
+
import insertHtmlIntoElement from '@claspo/common/dom/insertHtmlIntoElement';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_PARENT_CONTAINER_STYLES = {
|
|
5
|
+
maxWidth: '100%',
|
|
6
|
+
width: 'fit-content',
|
|
7
|
+
display: 'inline-flex',
|
|
8
|
+
fontFamily: 'inherit',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const DEFAULT_INNER_STYLES = {
|
|
12
|
+
padding: '0 5px',
|
|
13
|
+
display: 'inline-block',
|
|
14
|
+
maxWidth: 'calc(100% - 0px)',
|
|
15
|
+
textOverflow: 'ellipsis',
|
|
16
|
+
overflow: 'hidden',
|
|
17
|
+
whiteSpace: 'nowrap',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const DEFAULT_OUTER_STYLES = {
|
|
21
|
+
display: 'inline-flex',
|
|
22
|
+
justifyContent: 'center',
|
|
23
|
+
minHeight: 'fit-content',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function createStaticTextRoller(container, props) {
|
|
27
|
+
const id = props.id;
|
|
28
|
+
|
|
29
|
+
const longestOption = getLongestOptionByRenderedTextWidth(props.options);
|
|
30
|
+
const innerStyles = { ...DEFAULT_INNER_STYLES, ...props.styleAttributes };
|
|
31
|
+
|
|
32
|
+
const optionElements = props.options.map((option) => {
|
|
33
|
+
const optionElement = document.createElement('span');
|
|
34
|
+
optionElement.classList.add('opt');
|
|
35
|
+
const inner = document.createElement('span');
|
|
36
|
+
inner.innerText = option;
|
|
37
|
+
optionElement.appendChild(inner);
|
|
38
|
+
|
|
39
|
+
const containerParentStyles = window.getComputedStyle(container.parentElement);
|
|
40
|
+
|
|
41
|
+
applyStyles(inner, innerStyles);
|
|
42
|
+
|
|
43
|
+
applyStyles(
|
|
44
|
+
optionElement,
|
|
45
|
+
{
|
|
46
|
+
display: 'inline-flex',
|
|
47
|
+
justifyContent: containerParentStyles['text-align'] || DEFAULT_OUTER_STYLES.justifyContent,
|
|
48
|
+
},
|
|
49
|
+
longestOption,
|
|
50
|
+
innerStyles
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return optionElement;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const containerStyles = { ...DEFAULT_PARENT_CONTAINER_STYLES };
|
|
57
|
+
const innerContainerElement = document.createElement('span');
|
|
58
|
+
innerContainerElement.classList.add('container');
|
|
59
|
+
|
|
60
|
+
const paddings = {
|
|
61
|
+
paddingLeft: props.styleAttributes.borderLeftWidth || 0,
|
|
62
|
+
paddingRight: props.styleAttributes.borderRightWidth || 0,
|
|
63
|
+
paddingTop: props.styleAttributes.borderTopWidth || 0,
|
|
64
|
+
paddingBottom: props.styleAttributes.borderBottomWidth || 0,
|
|
65
|
+
};
|
|
66
|
+
applyStyles(innerContainerElement, paddings);
|
|
67
|
+
|
|
68
|
+
const hiddenPlaceholderElement = document.createElement('span');
|
|
69
|
+
hiddenPlaceholderElement.innerText = longestOption;
|
|
70
|
+
|
|
71
|
+
applyStyles(hiddenPlaceholderElement, {
|
|
72
|
+
...innerStyles,
|
|
73
|
+
visibility: 'hidden',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const optsElement = document.createElement('span');
|
|
77
|
+
optsElement.classList.add('opts');
|
|
78
|
+
optionElements.forEach((optionElement) => {
|
|
79
|
+
optsElement.appendChild(optionElement);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
innerContainerElement.append(hiddenPlaceholderElement, optsElement);
|
|
83
|
+
insertHtmlIntoElement({
|
|
84
|
+
element: container,
|
|
85
|
+
html: '',
|
|
86
|
+
});
|
|
87
|
+
container.appendChild(innerContainerElement);
|
|
88
|
+
|
|
89
|
+
applyStyles(container, containerStyles);
|
|
90
|
+
|
|
91
|
+
const calculatedOptionHeight = innerContainerElement.querySelectorAll('.opt')[0].offsetHeight;
|
|
92
|
+
const newHeight = `${calculatedOptionHeight}px`;
|
|
93
|
+
|
|
94
|
+
innerContainerElement.style.height = newHeight;
|
|
95
|
+
hiddenPlaceholderElement.style.height = newHeight;
|
|
96
|
+
|
|
97
|
+
container.classList.add('container--staticTextRoller');
|
|
98
|
+
|
|
99
|
+
const durations = prepareAnimationDurations(props);
|
|
100
|
+
createSlideDownAnimation(container, props, durations);
|
|
101
|
+
runAnimation(optsElement, props, durations);
|
|
102
|
+
|
|
103
|
+
return {element: container, id};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const DEFAULT_OPTION = 'Option 1';
|
|
107
|
+
|
|
108
|
+
function prepareAnimationDurations(props) {
|
|
109
|
+
const DEFAULT_TIME_PER_OPTION = (props.animationSpeedInSec || 1) * 1000; // customizable speed in seconds, converted to ms
|
|
110
|
+
const optionTransitionTime = 500; //ms
|
|
111
|
+
const MAX_CHARACTERS_PER_WORD = 15;
|
|
112
|
+
const longestOptionLength = Math.max(...props.options.map(option => option.replace(/\s+/g, '').length));
|
|
113
|
+
const longestOptionFractions = Math.max(Math.ceil(longestOptionLength / MAX_CHARACTERS_PER_WORD), 1);
|
|
114
|
+
const optionsCount = props.options.length;
|
|
115
|
+
const optionTime = longestOptionFractions * DEFAULT_TIME_PER_OPTION;
|
|
116
|
+
const timePerOption = optionTime + optionTransitionTime;
|
|
117
|
+
const duration = optionsCount * timePerOption;
|
|
118
|
+
const optionTimePercentage = 100 / optionsCount;
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
optionTimePercentage,
|
|
122
|
+
duration,
|
|
123
|
+
longestOptionFractions
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function createUpdatingTextRoller(container, props) {
|
|
128
|
+
const id = props.id;
|
|
129
|
+
const outer = document.createElement('span');
|
|
130
|
+
outer.classList.add('outer');
|
|
131
|
+
|
|
132
|
+
const longestOption = getLongestOptionByRenderedTextWidth(props.options);
|
|
133
|
+
|
|
134
|
+
const inner = document.createElement('span');
|
|
135
|
+
inner.classList.add('inner');
|
|
136
|
+
// use textContent to prevent HTML injection from options
|
|
137
|
+
inner.textContent = props.options.at(0) || DEFAULT_OPTION;
|
|
138
|
+
inner.setAttribute('contenteditable', 'false');
|
|
139
|
+
outer.appendChild(inner);
|
|
140
|
+
|
|
141
|
+
const containerParentStyles = window.getComputedStyle(container.parentElement);
|
|
142
|
+
const containerTextStyleAttributes = getElementTextRelatedStyleAttributes(containerParentStyles);
|
|
143
|
+
|
|
144
|
+
const innerStyles = {
|
|
145
|
+
...DEFAULT_INNER_STYLES,
|
|
146
|
+
...props.styleAttributes,
|
|
147
|
+
...containerTextStyleAttributes
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
applyStyles(inner, innerStyles);
|
|
151
|
+
applyStyles(
|
|
152
|
+
outer,
|
|
153
|
+
{
|
|
154
|
+
...DEFAULT_OUTER_STYLES,
|
|
155
|
+
borderRadius: inner.style['border-radius'] || DEFAULT_OUTER_STYLES.borderRadius,
|
|
156
|
+
justifyContent: containerParentStyles['text-align'] || DEFAULT_OUTER_STYLES.justifyContent,
|
|
157
|
+
},
|
|
158
|
+
longestOption,
|
|
159
|
+
innerStyles,
|
|
160
|
+
parseFloat(containerParentStyles.width)
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
insertHtmlIntoElement({
|
|
164
|
+
element: container,
|
|
165
|
+
html: '',
|
|
166
|
+
});
|
|
167
|
+
applyStyles(container, { ...DEFAULT_PARENT_CONTAINER_STYLES });
|
|
168
|
+
container.appendChild(outer);
|
|
169
|
+
|
|
170
|
+
return {element: container, id};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function runAnimation(element, props, durations) {
|
|
174
|
+
const duration = durations.duration / 1000; // in sec
|
|
175
|
+
// if 1 element don't start animation
|
|
176
|
+
if (props.options.length > 1) {
|
|
177
|
+
element.style.animation = `slide-down ${duration}s infinite`;
|
|
178
|
+
} else {
|
|
179
|
+
element.style.transform = 'translateY(0%)';
|
|
180
|
+
element.querySelectorAll('.opt')[0].style.display = 'none';
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function applyStyles(element, styles, longestOption, childStyles, elementParentWidth) {
|
|
185
|
+
let defaultStyles = {};
|
|
186
|
+
if (!!longestOption) {
|
|
187
|
+
const width = getTextWidth(longestOption, styles, childStyles);
|
|
188
|
+
const minWidth = `${(width > elementParentWidth) ? width : Math.min(width, elementParentWidth)}px`;
|
|
189
|
+
|
|
190
|
+
defaultStyles = {
|
|
191
|
+
minWidth
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
Object.entries({...defaultStyles, ...styles} || {}).forEach(([key, value]) => {
|
|
196
|
+
element.style[key] = value;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function getTextWidth(text, styles = {}, childStyles = {}) {
|
|
201
|
+
const out = document.createElement('span');
|
|
202
|
+
const inn = document.createElement('span');
|
|
203
|
+
inn.innerText = text;
|
|
204
|
+
out.appendChild(inn);
|
|
205
|
+
|
|
206
|
+
for (const [key, value] of Object.entries(childStyles)) {
|
|
207
|
+
inn.style[key] = value;
|
|
208
|
+
}
|
|
209
|
+
for (const [key, value] of Object.entries(styles)) {
|
|
210
|
+
out.style[key] = value;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
out.style.visibility = 'hidden';
|
|
214
|
+
document.body.appendChild(out);
|
|
215
|
+
|
|
216
|
+
const { width } = out.getBoundingClientRect();
|
|
217
|
+
|
|
218
|
+
document.body.removeChild(out);
|
|
219
|
+
|
|
220
|
+
return width;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function createSlideDownAnimation(container, props, durations) {
|
|
224
|
+
const GAP_BETWEEN_ITEMS = 1; // 1px
|
|
225
|
+
const MAX_GAP_BETWEEN_ITEMS = (props.options.length - 1) * GAP_BETWEEN_ITEMS;
|
|
226
|
+
const transitionSpeedMultiplier = durations.longestOptionFractions > 1 ? 1.5 : 3;
|
|
227
|
+
const transitionPercentage = durations.optionTimePercentage / transitionSpeedMultiplier;
|
|
228
|
+
|
|
229
|
+
// second keyframe is for the pause
|
|
230
|
+
const keyframes = flat(props.options.map((_, index) => {
|
|
231
|
+
return `
|
|
232
|
+
${index * durations.optionTimePercentage}% {
|
|
233
|
+
transform: translateY(calc(${index * 100}% + ${index * GAP_BETWEEN_ITEMS}px));
|
|
234
|
+
}
|
|
235
|
+
${index * durations.optionTimePercentage + transitionPercentage}% {
|
|
236
|
+
transform: translateY(calc(${index * 100}% + ${index * GAP_BETWEEN_ITEMS}px));
|
|
237
|
+
}`;
|
|
238
|
+
})).concat(`
|
|
239
|
+
100% {
|
|
240
|
+
transform: translateY(calc(${props.options.length * 100}% + ${MAX_GAP_BETWEEN_ITEMS}px));
|
|
241
|
+
}
|
|
242
|
+
`).join('\n');
|
|
243
|
+
|
|
244
|
+
const style = document.createElement('style');
|
|
245
|
+
insertHtmlIntoElement({
|
|
246
|
+
element: style,
|
|
247
|
+
html: `@keyframes slide-down {${keyframes}}`,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const firstOption = container.querySelector('.opt');
|
|
251
|
+
const clonedFirstOption = firstOption.cloneNode(true);
|
|
252
|
+
|
|
253
|
+
container.appendChild(style);
|
|
254
|
+
container.querySelector('.opts').appendChild(clonedFirstOption);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
function getLongestOptionByRenderedTextWidth(options) {
|
|
259
|
+
return options
|
|
260
|
+
.map(o => ({
|
|
261
|
+
value: o,
|
|
262
|
+
width: getTextWidth(o)
|
|
263
|
+
}))
|
|
264
|
+
.sort((a, b) => b.width - a.width)
|
|
265
|
+
.at(0)
|
|
266
|
+
.value;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function getElementTextRelatedStyleAttributes(elStyles) {
|
|
270
|
+
let elTextStyleAttributes = {};
|
|
271
|
+
const textStyleProperties = [
|
|
272
|
+
'font-size',
|
|
273
|
+
'font-weight',
|
|
274
|
+
'font-style',
|
|
275
|
+
'font-family',
|
|
276
|
+
'text-align',
|
|
277
|
+
'text-shadow',
|
|
278
|
+
'letter-spacing',
|
|
279
|
+
'line-height'
|
|
280
|
+
];
|
|
281
|
+
|
|
282
|
+
Object.keys(elStyles)
|
|
283
|
+
.forEach((idx) => {
|
|
284
|
+
if (!Number.isInteger(Number(idx))) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const cssPropertyName = elStyles[idx];
|
|
288
|
+
if (!textStyleProperties.includes(cssPropertyName)) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
elTextStyleAttributes[cssPropertyName] = elStyles.getPropertyValue(cssPropertyName);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
return elTextStyleAttributes;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export { createStaticTextRoller, createUpdatingTextRoller };
|