@claspo/components 1.6.3 → 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.
Files changed (26) hide show
  1. package/package.json +4 -2
  2. package/script/SysButtonComponent/SysButton.manifest.js +126 -0
  3. package/script/SysButtonComponent/SysButton.styles.js +64 -0
  4. package/script/SysButtonComponent/SysButtonComponent.js +231 -0
  5. package/script/SysColumnComponent/SysColumn.manifest.js +17 -0
  6. package/script/SysColumnComponent/SysColumnComponent.js +107 -0
  7. package/script/SysColumnsComponent/SysColumns.manifest.js +17 -0
  8. package/script/SysColumnsComponent/SysColumnsComponent.js +53 -0
  9. package/script/SysColumnsComponent/getStyleElement.js +23 -0
  10. package/script/SysContainerComponent/SysBaseContainerComponent.js +41 -0
  11. package/script/SysContainerComponent/SysContainer.manifest.js +18 -0
  12. package/script/SysContainerComponent/SysContainerComponent.js +86 -0
  13. package/script/SysImageComponent/SysImage.manifest.js +18 -0
  14. package/script/SysImageComponent/SysImageComponent.js +378 -0
  15. package/script/SysImageComponent/getStyleElement.js +18 -0
  16. package/script/SysInputComponent/EmailSuggesting.js +252 -0
  17. package/script/SysInputComponent/InputFormControl.js +136 -0
  18. package/script/SysInputComponent/SysInput.manifest.js +728 -0
  19. package/script/SysInputComponent/SysInputComponent.js +84 -0
  20. package/script/SysInputComponent/emailProvidersList.js +158 -0
  21. package/script/SysInputComponent/getOverlayStyles.js +220 -0
  22. package/script/SysInputComponent/getStyleElement.js +69 -0
  23. package/script/SysInputComponent/inputValidators.js +293 -0
  24. package/script/SysTextComponent/SysText.manifest.js +29 -0
  25. package/script/SysTextComponent/SysTextComponent.js +147 -0
  26. 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 };