@massimo-cassandro/minimo 0.8.8 → 0.9.1

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/README.md CHANGED
@@ -11,3 +11,22 @@ npm i @massimo-cassandro/minimo
11
11
  ```
12
12
 
13
13
  > Note: **minimo** is designed to be used with WebPack using the configuration you can find in the [webpack-setup folder](./webpack-setup/) of this repository. If changes are made (for example, to how SVG files are imported), things may not work as expected
14
+
15
+
16
+ ## Utilities
17
+
18
+ ### Dom builder
19
+
20
+ Builds dom elements from array of objects.
21
+
22
+ Only available as module script.
23
+
24
+ ```
25
+ import { domBuilder } from '@massimo.cassandro/dom-builder';
26
+
27
+ domBuilder({ /* options */}); // see source code for details
28
+ ```
29
+
30
+ ### Classnames
31
+
32
+ TODO
@@ -0,0 +1,35 @@
1
+ // tokens utitilizzati sia da snackaber che da toast
2
+ export default{
3
+ snackbar: {
4
+
5
+
6
+
7
+
8
+
9
+
10
+ font: {
11
+ size: {
12
+ $value: '{font.size.sm}',
13
+ $type: 'dimension'
14
+ },
15
+ },
16
+
17
+ border: {
18
+ radius: {
19
+ $value: '{radius.sm}',
20
+ $type: 'color'
21
+ }
22
+ },
23
+ text: {
24
+ color: {
25
+ $value: '{text.color}',
26
+ $type: 'color'
27
+ }
28
+ },
29
+ duration: {
30
+ $value: '5s',
31
+ $type: 'duration'
32
+ }
33
+ }
34
+
35
+ };
package/index.js CHANGED
@@ -1 +1,2 @@
1
1
  export { classnames } from './src/utilities/classnames.js';
2
+ export { domBuilder } from './src/utilities/dom-builder/dom-builder.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@massimo-cassandro/minimo",
3
- "version": "0.8.8",
4
- "description": "Light css framework",
3
+ "version": "0.9.1",
4
+ "description": "Light css framework & utilities",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/massimo-cassandro/minimo#readme",
7
7
  "bugs": {
@@ -19,6 +19,7 @@
19
19
  "UPD-version": "npx update-version # --config=./dev-utilities.config.mjs",
20
20
  "upd@m": "npx upd@m",
21
21
  "npm-publish": "npm publish",
22
+ "webpack": "NODE_ENV=development webpack serve --config ./demo/demo-src/webpack.config.mjs",
22
23
  "build-tokens": "node ./design-tokens-utilities/build-tokens.mjs --config ./tokens-config.mjs",
23
24
  "check unresolved props": "node ./design-tokens-utilities/check-unresolved-custom-props.mjs --config ./tokens-config.mjs"
24
25
  },
@@ -28,6 +29,16 @@
28
29
  "access": "public"
29
30
  },
30
31
  "main": "./index.js",
32
+ "sideEffects": [
33
+ "*.css",
34
+ "*.js",
35
+ "*.svg",
36
+ "*.jpg",
37
+ "*.jpeg",
38
+ "*.png",
39
+ "*.webp",
40
+ "*.avif"
41
+ ],
31
42
  "files": [
32
43
  "./index.js",
33
44
  "src/**/*.{js,mjs,css,svg,md}",
@@ -36,9 +47,36 @@
36
47
  "design-tokens-utilities/**/*.{mjs,md}"
37
48
  ],
38
49
  "devDependencies": {
50
+ "@babel/core": "^7.29.0",
51
+ "@babel/preset-env": "^7.29.2",
52
+ "@csstools/postcss-global-data": "^4.0.0",
39
53
  "@massimo-cassandro/dev-updater": "^3.0.3",
40
54
  "@massimo-cassandro/eslint-config": "^3.0.6",
41
55
  "@massimo-cassandro/stylelint-config": "^2.3.4",
42
- "style-dictionary": "^5.4.0"
56
+ "autoprefixer": "^10.5.0",
57
+ "babel-loader": "^10.1.1",
58
+ "css-loader": "^7.1.4",
59
+ "css-minimizer-webpack-plugin": "^8.0.0",
60
+ "cssnano": "^7.1.5",
61
+ "ejs-loader": "^0.5.0",
62
+ "html-loader": "^5.1.0",
63
+ "html-webpack-plugin": "^5.6.7",
64
+ "mini-css-extract-plugin": "^2.10.2",
65
+ "mini-svg-data-uri": "^1.4.4",
66
+ "postcss": "^8.5.10",
67
+ "postcss-custom-media": "^12.0.1",
68
+ "postcss-loader": "^8.2.1",
69
+ "postcss-preset-env": "^11.2.1",
70
+ "process": "^0.11.10",
71
+ "style-dictionary": "^5.4.0",
72
+ "style-loader": "^4.0.0",
73
+ "svg-url-loader": "^8.0.0",
74
+ "svgo": "^4.0.1",
75
+ "svgo-add-viewbox": "^3.1.0",
76
+ "svgo-loader": "^5.0.0",
77
+ "terser-webpack-plugin": "^5.4.0",
78
+ "webpack": "^5.106.2",
79
+ "webpack-cli": "^7.0.2",
80
+ "webpack-dev-server": "^5.2.3"
43
81
  }
44
82
  }
@@ -0,0 +1,2 @@
1
+ da rivedere
2
+ spostare custome props nel dictionary
@@ -1,4 +1,3 @@
1
- /* NB incluso nel css principale */
2
1
 
3
2
  @import '@massimo-cassandro/modal-alert';
4
3
 
@@ -13,8 +12,8 @@
13
12
  --malert-warning-color: var(--status-warning-color);
14
13
  --malert-warning-color-fg: var(--status-warning-fg);
15
14
 
16
- --malert-error-color: var(--status-danger-color);
17
- --malert-error-color-fg: var(--status-danger-fg);
15
+ --malert-danger-color: var(--status-danger-color);
16
+ --malert-danger-color-fg: var(--status-danger-fg);
18
17
 
19
18
  --malert-confirm-color: var(--status-warning-color);
20
19
  --malert-confirm-color-fg: var(--status-warning-fg);
@@ -0,0 +1,105 @@
1
+ import { domBuilder, classnames } from '../../../index.js';
2
+ import * as styles from './core.module.css';
3
+
4
+ /*
5
+ References
6
+ * https://web.dev/learn/css/popover-and-dialog
7
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/popover
8
+ * https://developer.mozilla.org/en-US/docs/Web/API/Popover_API
9
+ * https://developer.mozilla.org/en-US/docs/Web/API/Popover_API/Using
10
+
11
+ * https://m3.material.io/components/snackbar/guidelines
12
+
13
+ */
14
+
15
+ export function snackbar(message, options = {}){
16
+
17
+ options = {
18
+
19
+ status : null, // danger, warning, info, success
20
+ duration : 4000, // ms or false/null to avoid autoclose
21
+ close_btn : true, // true | false
22
+
23
+ // TODO snackbar action
24
+ action : null, // null or function
25
+ action_text : null, // null or string
26
+
27
+ ...options
28
+ };
29
+
30
+ domBuilder([
31
+ {
32
+ className: classnames(styles.snackbar, options.status? styles[`status-${options.status}`] : null),
33
+ attrs: {
34
+ popover: 'manual',
35
+ role: options.action? 'alert' : 'status'
36
+ },
37
+ children: [
38
+ {
39
+ className: styles.text,
40
+ content: message
41
+ },
42
+ {
43
+ tag: 'button',
44
+ attrs: {
45
+ type: 'button',
46
+ },
47
+ className: classnames('btn-reset', styles.action),
48
+ content: options.action_text?? '_actions_',
49
+ condition: options.action && (typeof options.action === 'function'),
50
+ callback: el => {}
51
+ },
52
+ {
53
+ tag: 'button',
54
+ className: classnames('btn-close', styles.closeBtn),
55
+ attrs: {
56
+ type: 'button',
57
+ },
58
+ }
59
+ ]
60
+ }
61
+ ], document.body);
62
+
63
+ }
64
+
65
+ /*
66
+ export const NotificationCore = {
67
+
68
+ createNotificationElement(message, options = {}) {
69
+
70
+
71
+
72
+ const show = () => {
73
+ document.body.appendChild(el);
74
+ el.showPopover();
75
+ setTimeout(() => hide(), duration);
76
+ };
77
+
78
+ const hide = () => {
79
+ if (el.matches(':popover-open')) {
80
+ el.hidePopover();
81
+ el.remove(); // Pulizia del DOM
82
+ }
83
+ };
84
+
85
+ return { el, show, hide };
86
+ }
87
+ };
88
+
89
+
90
+
91
+
92
+ toast
93
+
94
+ import { NotificationCore } from './notifications.js';
95
+
96
+ export const toast = (message, type = 'info') => {
97
+ const { el, show } = NotificationCore.createBase(message, { type, duration: 4000 });
98
+
99
+ el.classList.add('ui-toast');
100
+
101
+ show();
102
+ };
103
+
104
+
105
+ */
@@ -0,0 +1,70 @@
1
+ /* purgecss start ignore */
2
+
3
+ .snackbar {
4
+
5
+ }
6
+
7
+ /* purgecss end ignore */
8
+
9
+
10
+ /*
11
+
12
+
13
+
14
+
15
+
16
+ .toastWrapper {
17
+ position: fixed;
18
+ inset-block-end: 0;
19
+ inset-inline: 0;
20
+ z-index: 1;
21
+
22
+ display: grid;
23
+ gap: 1vh;
24
+ justify-content: center;
25
+ justify-items: stretch;
26
+ padding-block-end: 5vh;
27
+
28
+ pointer-events: none;
29
+ }
30
+
31
+ .toast {
32
+ --_bg-lightness: 90%;
33
+ --_travel-distance: 0;
34
+
35
+ max-inline-size: min(25ch, 90vw);
36
+ padding-block: .5ch;
37
+ padding-inline: 1ch;
38
+
39
+ font-size: var(--toast-font-size);
40
+ color: var(--toast-text-color);
41
+ background: hsl(0 0% var(--_bg-lightness) / 90%);
42
+ border-radius: var(--toast-border-radius);
43
+ animation:
44
+ fade-in .3s ease,
45
+ slide-in .3s ease,
46
+ fade-out .3s ease var(--toast-duration);
47
+
48
+ will-change: transform;
49
+
50
+ @media (--dark) {
51
+ --_bg-lightness: 20%;
52
+ }
53
+
54
+ @media (--motionOK) {
55
+ --_travel-distance: 5vh;
56
+ }
57
+ }
58
+
59
+ @keyframes fade-in {
60
+ from { opacity: 0; }
61
+ }
62
+
63
+ @keyframes fade-out {
64
+ to { opacity: 0; }
65
+ }
66
+
67
+ @keyframes slide-in {
68
+ from { transform: translateY(var(--_travel-distance, 10px)); }
69
+ }
70
+ */
@@ -24,7 +24,7 @@
24
24
  --------------------------
25
25
  */
26
26
 
27
- /* alert-error / alert-danger (default) */
27
+ /* alert-danger (default) */
28
28
  --_alert-bg-color: var(--status-danger-background-color);
29
29
  --_alert-fg-color: var(--status-danger-fg);
30
30
  --_alert-border-color: var(--status-danger-border-color);
@@ -20,7 +20,11 @@ a:not(.btn) {
20
20
  }
21
21
 
22
22
  a:not(.btn), .btn-link {
23
- &:focus, &:focus-visible {
23
+ &:focus {
24
+ outline: none;
25
+ }
26
+
27
+ &:focus-visible {
24
28
  outline: 1px solid color-mix(in srgb, var(--link-color) 60%, transparent);
25
29
  outline-offset: 2px;
26
30
  }
@@ -41,6 +41,10 @@
41
41
  }
42
42
 
43
43
  &:focus {
44
+ outline: none;
45
+ }
46
+
47
+ &:focus-visible {
44
48
  outline: var(--btn-close-focus-outline-width) solid var(--btn-close-focus-outline-color);
45
49
  outline-offset: var(--btn-close-focus-outline-offset);
46
50
  }
@@ -11,8 +11,11 @@
11
11
  --_button-border-color: var(--btn-primary-hover-border-color);
12
12
  }
13
13
  }
14
+ &:focus {
15
+ outline: none;
16
+ }
14
17
 
15
- &:focus, &:active {
18
+ &:focus-visible, &:active {
16
19
  outline: var(--btn-focus-outline-width) solid var(--btn-primary-focus-outline-color);
17
20
  /* outline-offset: 4px; */
18
21
  }
@@ -12,7 +12,11 @@
12
12
  }
13
13
  }
14
14
 
15
- &:focus, &:active {
15
+ &:focus {
16
+ outline: none;
17
+ }
18
+
19
+ &:focus-visible, &:active {
16
20
  outline: var(--btn-focus-outline-width) solid var(--btn-secondary-focus-outline-color);
17
21
  /* outline-offset: 4px; */
18
22
  }
@@ -22,7 +22,11 @@ button svg {
22
22
  }
23
23
 
24
24
  a.btn {
25
- &, &:hover, &:active, &:focus, &:visited {
25
+ &:focus {
26
+ outline: none;
27
+ }
28
+
29
+ &, &:hover, &:active, &:focus-visible, &:visited {
26
30
  color: var(--_button-fg-color);
27
31
  text-decoration: none;
28
32
  }
@@ -18,13 +18,13 @@
18
18
  --_status-fg-color: var(--status-info-fg);
19
19
  --_status-border-color: var(--status-info-border-color);
20
20
  }
21
- &.btn-danger, &.btn-error {
21
+ &.btn-danger {
22
22
  --_status-bg-color: var(--status-danger-color);
23
23
  --_status-fg-color: var(--status-danger-fg);
24
24
  --_status-border-color: var(--status-danger-border-color);
25
25
  }
26
26
 
27
- &.btn-success, &.btn-info, &.btn-danger, &.btn-error {
27
+ &.btn-success, &.btn-info, &.btn-danger {
28
28
  --_status-hover-bg-color: color-mix(in srgb, var(--_status-bg-color) 30%, transparent);
29
29
  --_status-hover-fg-color: var(--_status-bg-color);
30
30
  --_status-hover-border-color: var(--_status-bg-color);
@@ -51,7 +51,7 @@
51
51
 
52
52
  }
53
53
 
54
- &.btn-success, &.btn-info, &.btn-danger, &.btn-error, &.btn-warning {
54
+ &.btn-success, &.btn-info, &.btn-danger, &.btn-warning {
55
55
 
56
56
  --_button-bg-color: var(--_status-bg-color);
57
57
  --_button-fg-color: var(--_status-fg-color);
@@ -74,7 +74,11 @@
74
74
  }
75
75
  }
76
76
 
77
- &:focus, &:active {
77
+ &:focus {
78
+ outline: none;
79
+ }
80
+
81
+ &:focus-visible, &:active {
78
82
  outline: var(--_status-focus-outline);
79
83
  }
80
84
 
@@ -41,6 +41,10 @@
41
41
  border-radius: var(--form-control-border-radius);
42
42
 
43
43
  &:focus {
44
+ outline: none;
45
+ }
46
+
47
+ &:focus-visible {
44
48
  outline: var(--form-control-focus-outline-width) solid var(--form-control-focus-color);
45
49
  outline-offset: var(--form-control-focus-outline-offset);
46
50
  }
@@ -33,7 +33,11 @@
33
33
  }
34
34
 
35
35
  > * {
36
- &:focus, &:focus-within {
36
+ &:focus {
37
+ outline: none;
38
+ }
39
+
40
+ &:focus-visible, &:focus-within {
37
41
  z-index: 5;
38
42
  }
39
43
  }
@@ -18,6 +18,10 @@
18
18
  outline-offset: var(--form-validation-outline-offset);
19
19
 
20
20
  &:focus {
21
+ outline: none;
22
+ }
23
+
24
+ &:focus-visible {
21
25
  /* background-color: var(--_status-color); */
22
26
  outline: var(--form-control-focus-outline-width) solid var(--_status-color);
23
27
  outline-offset: var(--form-control-focus-outline-offset);
package/src/css/text.css CHANGED
@@ -19,7 +19,11 @@ a {
19
19
  }
20
20
 
21
21
  a, .btn-link {
22
- &:focus, &:focus-visible {
22
+ &:focus {
23
+ outline: none;
24
+ }
25
+
26
+ &:focus-visible {
23
27
  outline: 1px solid color-mix(in srgb, var(--link-color) 60%, transparent);
24
28
  outline-offset: 2px;
25
29
  }
@@ -0,0 +1,184 @@
1
+ import { domBuilderBasicSetup } from './domBuilderBasicSetup.js';
2
+ import { parseDomString } from './parseDomString.js';
3
+
4
+ // TODO sintassi stringa su più righe in cui ogni riga corrisponde ad un elemento
5
+ // TODO sintassi stringa come il precedente, ma con possibilità di indicare una nidificazione (indent?)
6
+
7
+ /**
8
+ * domBuilder
9
+ * Costruisce una struttura DOM a partire da un array di configurazione.
10
+ *
11
+ * La funzione analizza un array di oggetti di configurazione per creare elementi HTML.
12
+ *
13
+ * **Formato della Struttura di Configurazione (structureArray):**
14
+ *
15
+ * L'array `structureArray` può contenere stringhe (con sintassi abbreviata) e/o oggetti di configurazione:
16
+ *
17
+ * ```javascript
18
+ * structure = [
19
+ * '#mainContainer.container',
20
+ * 'p#main-info.info.active{data-id:123,role=button} Lorem ipsum',
21
+ * {
22
+ * tag: 'div' | ['.divClass', 'h2#id', 'table.class1', 'thead', 'tr.class2.class3(attr1: attrValue)'], // anche tagName
23
+ * className: 'xxx' | ['class1', 'class2'], // anche `class`
24
+ * id: 'element-id',
25
+ * attrs: [attr_name, attr_value] | [[...], [...]] | {name: value},
26
+ * content: 'xxx' | 123 | function | Element,
27
+ * condition: true | false,
28
+ * callback: el => ...,
29
+ * children: [...]
30
+ * },
31
+ * 'div.class[data-xxx: value]',
32
+ * '.another-div',
33
+ * 'p#paragraph''
34
+ * '...',
35
+ * { ... }
36
+ * ]
37
+ * ```
38
+ *
39
+ * id e classi possono essere specificati sia chiave dell'oggetto di configurazione che all'interno dell'oggetto `attrs`
40
+ * In caso di conflitto, prevalgono quelli indicati al livello principale.
41
+ *
42
+ * È inoltre possibile utilizzare una versione "abbreviata" utilizzando una stringa costruita secondo la sintassi di `parseDomString`.
43
+ * Vedi `parseDomString` per altre in.formazioni.
44
+ *
45
+ * Questo tipo di sintassi non prevede la possibilità di definire `content`, `callback`, `condition` e `children`: in questi casi è necessario utilizzare la sintassi di base.
46
+ *
47
+ * La sintassi abbreviata può essere utilizzata anche con l'elemento `tag` della sintassi ad oggetti.
48
+ * È possibile definirlo come array in cui ogni elemento corrisponde al genitore del successivo. Eventuali altre proprietà dell'oggetto
49
+ * (className, callback, ecc.) vengono applicate solo all'ultimo elemento dell'array.
50
+ * In caso di conflitto tra classi, id o attributi indicati nella stringa e quelli indicati nell'oggetto
51
+ * prevalgono questi ultimi.
52
+ *
53
+ * @function domBuilder
54
+ * @param {Array<Object|string>} [structureArray=[]] - Array di configurazione degli elementi. Può contenere stringhe (con sintassi abbreviata secondo `parseDomString`) e/o oggetti di configurazione.
55
+ * @param {string|Array<string>} [structureArray[].tag='div'] - Nome del tag HTML (es. 'div', 'p') o array di tag nidificati (es. ['div.container', 'section', 'article']).
56
+ * @param {string|Array<string>} [structureArray[].tagName='div'] - Alias di tag
57
+ * @param {string|Array<string>} [structureArray[].className] - Classe/i CSS: stringa singola o array di classi, in quest'ultimo caso, i valori *falsy* vengopno rimossi.
58
+ * @param {string|Array<string>} [structureArray[].class] - Alias di className.
59
+ * @param {string} [structureArray[].id] - ID univoco dell'elemento.
60
+ * @param {Array<[string, *]>|Array<Array<[string, *]>>|Object<string, *>} [structureArray[].attrs] - Attributi: coppia `[name, value]`, array di coppie o oggetto `{name: value}`.
61
+ * @param {string|number|Function|Element} [structureArray[].content] - Contenuto dell'elemento: stringa, numero, funzione che restituisce contenuto, o HTMLElement.
62
+ * @param {boolean} [structureArray[].condition=true] - Se false, l'elemento non viene generato.
63
+ * @param {Function} [structureArray[].callback] - Callback invocata quando l'elemento viene creato; riceve l'elemento come argomento.
64
+ * @param {Array<Object>} [structureArray[].children] - Array di configurazione per elementi figlio.
65
+ * @param {HTMLElement} [parent] - Elemento genitore al quale appendere la struttura.
66
+ * @param {Object} [options={}] - Opzioni di configurazione.
67
+ * @param {boolean} [options.emptyParent=true] - Se true, l'element parent indicato viene svuotato prima di procedere con ciclo builder
68
+ * @returns {HTMLElement|null} Il primo elemento creato, o null se nessun elemento è stato creato.
69
+ */
70
+
71
+
72
+ export function domBuilder(structureArray = [], parent, options = {}) {
73
+
74
+ options = {
75
+ emptyParent: false,
76
+ ...options
77
+ };
78
+
79
+ let mainElement = null, el, grand_parent = null;
80
+
81
+ if(parent && options.emptyParent) {
82
+ parent.innerHTML = '';
83
+ }
84
+
85
+ structureArray.forEach(item => {
86
+
87
+ if(item != null && typeof item === 'string' && item !== '') {
88
+ item = parseDomString(item);
89
+ }
90
+
91
+ // console.log(item);
92
+
93
+ if (item != null && (item.condition ?? true)) {
94
+
95
+
96
+ // se tag è un array, vengono creati una serie di elementi nidificati
97
+ // l'ultimo è quello a cui vengono applicate le altre proprietà dell'oggetto in esame
98
+ if (Array.isArray(item.tag)) {
99
+
100
+ grand_parent = parent;
101
+
102
+ item.tag.forEach((tagItem, idx) => {
103
+
104
+ const isLast = idx === item.tag.length - 1;
105
+ const parsedItem = parseDomString(tagItem);
106
+
107
+ // nel caso dell'ultimo elemento, merge con le opzioni dell'oggetto
108
+ // con precedenza alle proprietà di quest'ultimo
109
+ if(isLast) {
110
+ item.attrs = {...parsedItem.attrs??{}, ...item.attrs??{}};
111
+ }
112
+ el = domBuilderBasicSetup(
113
+ document.createElement(parsedItem.tag || 'div'),
114
+ {...parsedItem, ...(isLast? item??{} : {})}
115
+ );
116
+
117
+ if (!isLast) { // l'ultimo elemento è gestito dalla procedura "standard"
118
+ if (parent) {
119
+ parent.appendChild(el);
120
+ }
121
+ parent = el;
122
+ }
123
+
124
+ });
125
+
126
+ } else {
127
+ el = domBuilderBasicSetup(
128
+ document.createElement(item.tag ?? 'div'),
129
+ item
130
+ );
131
+ }
132
+
133
+
134
+ if (item.content != null) {
135
+
136
+ let content = '';
137
+ if (typeof item.content === 'function') {
138
+ content = item.content();
139
+
140
+ } else if (item.content != null) {
141
+ content = String(item.content);
142
+ }
143
+
144
+ if (content instanceof Element) {
145
+ el.appendChild(content);
146
+
147
+ } else {
148
+ el.innerHTML = content;
149
+ }
150
+ }
151
+
152
+
153
+ if (item.children != null && !Array.isArray(item.children)) {
154
+ // eslint-disable-next-line no-console
155
+ console.error('Error: `item.children` must be an array → ' + item.children);
156
+ }
157
+ if (item.children && Array.isArray(item.children)) {
158
+ domBuilder(item.children, el, {emptyParent: false});
159
+ }
160
+
161
+ if (mainElement == null) {
162
+ mainElement = el;
163
+ }
164
+
165
+ if (parent) {
166
+ parent.appendChild(el);
167
+ }
168
+
169
+ // TODO callback che includano azioni relatuvi ai children dell'elemento potrebbero bìnon essere eseguiti in assenza di parent
170
+ if (item.callback && typeof item.callback === 'function') {
171
+ item.callback(el);
172
+ }
173
+
174
+ if (grand_parent) {
175
+ parent = grand_parent;
176
+ }
177
+ } // end if ((item.condition ?? true) && item != null)
178
+
179
+ }); // end forEach
180
+
181
+ return mainElement;
182
+ }
183
+
184
+
@@ -0,0 +1,75 @@
1
+
2
+ /**
3
+ * Esegue le operazione di base del domBuilder: assegnazione classi, id ecc...
4
+ */
5
+ export function domBuilderBasicSetup (element, domBuilderItem) {
6
+
7
+ // classi
8
+ // possibili denominazioni: class o className
9
+ // se presente className, class viene ignorato
10
+ if (domBuilderItem.class && !domBuilderItem.className) {
11
+ domBuilderItem.className = domBuilderItem.class;
12
+ }
13
+
14
+ // se item.attrs è un array singolo viene trattato come singolo elemento
15
+ if (domBuilderItem.attrs && Array.isArray(domBuilderItem.attrs) && !Array.isArray(domBuilderItem.attrs[0])) {
16
+ domBuilderItem.attrs = [ domBuilderItem.attrs ];
17
+
18
+ // item.attrs può essere un oggetto del tipo {attr_name: attr_value}
19
+ } else if (typeof domBuilderItem.attrs === 'object' && !Array.isArray(domBuilderItem.attrs) && domBuilderItem.attrs !== null) {
20
+ domBuilderItem.attrs = Object.entries(domBuilderItem.attrs);
21
+ }
22
+
23
+ // gli attributi booleani non vengono aggiunti se il loro valore è false
24
+ const boolAttrs = [
25
+ 'allowfullscreen',
26
+ 'async',
27
+ 'autocomplete',
28
+ 'autofocus',
29
+ 'autoplay',
30
+ 'border',
31
+ 'checked',
32
+ 'compact',
33
+ 'contenteditable',
34
+ 'controls',
35
+ 'default',
36
+ 'loop',
37
+ 'defer',
38
+ 'disabled',
39
+ 'formnovalidate',
40
+ 'hidden',
41
+ 'inert',
42
+ 'ismap',
43
+ 'multiple',
44
+ 'muted',
45
+ 'novalidate',
46
+ 'open',
47
+ 'readonly',
48
+ 'required',
49
+ 'reversed',
50
+ 'selected',
51
+ 'spellcheck',
52
+ 'translate'
53
+ ];
54
+
55
+
56
+
57
+ (domBuilderItem.attrs ?? []).forEach(attr => {
58
+ if (attr[1] != null && !(boolAttrs.includes(attr[0]) && attr[1]=== false)) {
59
+ element.setAttribute(attr[0], String(attr[1]));
60
+ }
61
+ });
62
+
63
+ // assegnazioen classi e id (dopo attrs in modo che le proprietà a livello di oggetto prevalgano)
64
+ if(domBuilderItem.className) {
65
+ element.className = Array.isArray(domBuilderItem.className)
66
+ ? domBuilderItem.className.filter(Boolean).join(' ')
67
+ : domBuilderItem.className;
68
+ }
69
+ if(domBuilderItem.id) {
70
+ element.id = domBuilderItem.id;
71
+ }
72
+
73
+ return element;
74
+
75
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Analizza una stringa per estrarre il nome del tag, l'ID, le classi e gli attributi.
3
+ *
4
+ * * Il formato della stringa atteso è:
5
+ *
6
+ * `tag#id.classe1.classe2.classeN(attributo1: valore1, attributo2=valore2) Lorem ipsum`
7
+ *
8
+ * In cui:
9
+ * * `tag` è il nome dell'elemento, se non specificato si assume che sia un `div`
10
+ * * `id` è l'id, facoltativo, da assegnare all'elemento, deve essere preeceduto da `#` e deve essere posizionato subito dopo il tag
11
+ * * `classe1, classe2...` sono le classi, facoltative, devono essere precedute da `.`
12
+ * * `(...)` è il blocco degli attributi, facoltativo. Oltre che dalle parentesi tonde, questa parte può essere delimitata dalle quadre e dalle graffe.
13
+ * Ogni coppia _nome-valore_ va separata con `:` o `=`. Se presente solo il nome, si assume che il valore sia `true`.
14
+ * * `Lorem ipsum` indica un eventuale contenuto testuale, separato dal blocco precedente da uno spazio
15
+ *
16
+ * Restituisce un oggetto *child* domBuilder
17
+ *
18
+ * Esempio:
19
+ * > p#main-info.info.active{data-id:123,role=button} text content
20
+ *
21
+ * oppure
22
+ * > input#search-field[type=text,disabled]
23
+ *
24
+ * @param {string} domString La stringa DOM Builder da analizzare.
25
+ * @returns {{tagName: string, id: string, classes: string[], attributes: {[key: string]: string}}}
26
+ */
27
+
28
+ export function parseDomString(domString) {
29
+
30
+ // versione con groups
31
+ // const regex = new RegExp(
32
+ // /^(?<tag>[a-zA-Z]+)?/.source + // tag
33
+ // /(?:#(?<id>[a-zA-Z0-9_-]+))?/.source + // id
34
+ // /(?<classes>(?:\.[a-zA-Z0-9_-]+)*)?/.source + // classi
35
+ // /(?<attrs>[([{].*?[)\]}])?/.source + // attrs
36
+ // /(?: +(?<content>.*))?$/.source // content
37
+ // );
38
+
39
+ const regex = new RegExp(
40
+ /^([a-zA-Z]+)?/.source + // tag
41
+ /(?:#([a-zA-Z0-9_-]+))?/.source + // id
42
+ /((?:\.[a-zA-Z0-9_-]+)*)?/.source + // classi
43
+ /([([{].*?[)\]}])?/.source + // attrs
44
+ /(?: +(.*))?$/.source // content
45
+ );
46
+
47
+ const matches = domString.match(regex);
48
+
49
+
50
+ if (!matches) {
51
+ return null;
52
+ }
53
+
54
+ // Nuovi gruppi di cattura (match array):
55
+ // [0]: intera stringa
56
+ // [1]: Tag
57
+ // [2]: ID
58
+ // [3]: Classi
59
+ // [4]: Attributi (raw)
60
+ // [5]: content
61
+
62
+ const tag = (matches[1] || 'div').toLowerCase(),
63
+ id = matches[2]?.trim() || null,
64
+ classes = matches[3] ? matches[3].slice(1).split('.').map(c => c.trim()) : [],
65
+ rawAttrs = matches[4]?.trim() || null,
66
+ content = matches[5]?.trim() || null
67
+ ;
68
+
69
+ // versione con uso di `groups`
70
+ // const tag = (matches.groups.tag || 'div').toLowerCase(),
71
+ // id = matches.groups.id?.trim() || null,
72
+ // classes = matches.groups.classes? matches[3].slice(1).split('.').map(c => c.trim()) : [],
73
+ // rawAttrs = matches.groups.attrs?.trim() || null,
74
+ // content = matches.groups.content?.trim() || null
75
+ // ;
76
+
77
+ const attrs = {};
78
+ if (rawAttrs) {
79
+ const attrsContent = rawAttrs.substring(1, rawAttrs.length - 1);
80
+ const attrPairs = attrsContent.split(/\s*,\s*/);
81
+
82
+ attrPairs.forEach(pair => {
83
+ if (!pair) return;
84
+ if (pair.includes('=') || pair.includes(':')) {
85
+ const parts = pair.split(/[:=]/, 2),
86
+ name = parts[0]?.trim(),
87
+ value = parts[1] != null ? parts[1].trim() : true;
88
+
89
+ attrs[name] = value;
90
+
91
+ } else {
92
+
93
+ attrs[pair.trim()] = true;
94
+ }
95
+ });
96
+ }
97
+
98
+ return {
99
+ tag: tag,
100
+ id: id,
101
+ className: classes?.join(' '),
102
+ attrs: attrs,
103
+ content: content
104
+ };
105
+ }
@@ -1,6 +1,5 @@
1
1
 
2
2
  import { DataTable } from 'simple-datatables';
3
- // import { classnames } from '@massimo-cassandro/js-utilities/index.js';
4
3
 
5
4
  import { parseCols } from './src/parse-cols.js';
6
5
 
@@ -2,7 +2,7 @@
2
2
  // pre-elaborazione tipi dati predefiniti
3
3
  // le chiavi che cominciano con '_' sono di questa applicazione, le altre sono di simple-datatable
4
4
 
5
- import { classnames } from '@massimo-cassandro/js-utilities/index.js';
5
+ import { classnames } from '../../../../index.js';
6
6
 
7
7
  import * as styles from '../simple-datatable.module.css';
8
8