@massimo-cassandro/minimo 1.41.4 → 1.41.6
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 +12 -12
- package/charts/src/node-helpers/create-svg-canvas-node.mjs +2 -2
- package/charts/src/node-helpers/svg-text-to-path.js +9 -9
- package/charts/src/rating-display/index.js +1 -1
- package/charts/src/rating-display/ratindDisplayAnimation.js +3 -3
- package/design-tokens/README.md +2 -2
- package/design-tokens/_src/_readme.md +26 -26
- package/dev-tools/README.md +16 -16
- package/dev-tools/create-favicons/README.md +31 -31
- package/dev-tools/create-favicons/src/create-ico.mjs +2 -2
- package/dev-tools/create-favicons/src/default-params.mjs +1 -1
- package/dev-tools/create-favicons/src/init.mjs +1 -1
- package/dev-tools/design-tokens-builder/README.md +102 -102
- package/dev-tools/design-tokens-builder/build-tokens-src/build-source-modes.mjs +85 -73
- package/dev-tools/design-tokens-builder/build-tokens-src/config.mjs +109 -85
- package/dev-tools/design-tokens-builder/build-tokens-src/formats/css.mjs +113 -90
- package/dev-tools/design-tokens-builder/build-tokens-src/formats/json.mjs +107 -95
- package/dev-tools/design-tokens-builder/build-tokens-src/legacy-tokens-parser.mjs +71 -64
- package/dev-tools/design-tokens-builder/build-tokens-src/light-dark.mjs +75 -65
- package/dev-tools/design-tokens-builder/build-tokens-src/merge-css.mjs +113 -102
- package/dev-tools/design-tokens-builder/build-tokens-src/platforms.mjs +36 -30
- package/dev-tools/design-tokens-builder/build-tokens-src/resolve-source-paths.mjs +15 -13
- package/dev-tools/design-tokens-builder/build-tokens-src/source-prefixes.mjs +44 -42
- package/dev-tools/design-tokens-builder/build-tokens-src/source-transforms.mjs +89 -83
- package/dev-tools/design-tokens-builder/build-tokens-src/token-rename-parser.mjs +99 -86
- package/dev-tools/design-tokens-builder/build-tokens-src/transforms.mjs +64 -48
- package/dev-tools/design-tokens-builder/build-tokens.mjs +80 -68
- package/dev-tools/design-tokens-builder/check-unresolved-custom-props.mjs +103 -77
- package/dev-tools/design-tokens-builder/tokens-config-sample.mjs +275 -238
- package/dev-tools/find-orphan-css-classes/find-orphan-css-classes.mjs +2 -2
- package/dev-tools/starter-kit/source_files/app/CLAUDE.md +4 -2
- package/dev-tools/starter-kit/source_files/app/{css/index.css → index.css} +2 -2
- package/dev-tools/starter-kit/source_files/app/index.js +1 -1
- package/dev-tools/starter-kit/source_files/root/_sf/controller/ImgViewerController.php +3 -3
- package/dev-tools/starter-kit/source_files/root/_sf/controller/ViewerController.php +2 -2
- package/dev-tools/starter-kit/source_files/root/stylelint.config.mjs +1 -1
- package/dev-tools/starter-kit/source_files/root/webpack-config-modules/css-rules.mjs +1 -1
- package/dev-tools/starter-kit/source_files/root/webpack.config.mjs +15 -1
- package/dev-tools/starter-kit/webpack-config-upg.md +12 -0
- package/dev-tools/stylelint-config/README.md +7 -7
- package/dev-tools/svg-icons-tools/README.md +32 -32
- package/dev-tools/svg-icons-tools/src/config-manager.mjs +1 -1
- package/dev-tools/svg-icons-tools/src/default-config.mjs +118 -118
- package/dev-tools/svg-icons-tools/src/jsx-icon-file-builder.mjs +24 -24
- package/dev-tools/svg-icons-tools/src/parse-svg-filename.mjs +3 -3
- package/dev-tools/svg-icons-tools/src/parse-svg-files.mjs +1 -1
- package/dev-tools/svg-icons-tools/src/parse-svg-markup.mjs +4 -4
- package/dev-tools/svg-icons-tools/src/svg-to-jsx.mjs +2 -2
- package/dev-tools/svg-icons-tools/src/svg-to-optimized.mjs +2 -2
- package/dev-tools/svg-icons-tools/src/svg-to-symbols.mjs +2 -2
- package/dev-tools/upd@m/README.md +1 -1
- package/dev-tools/upd@m/index.mjs +1 -1
- package/dev-tools/update-version/README.md +9 -9
- package/package.json +1 -1
- package/snippets/unsplash-browser-app/README.md +1 -1
- package/snippets/unsplash-browser-app/unsplash-utilities.js +9 -9
- package/src/components/autocomplete/README.md +3 -3
- package/src/components/autocomplete/autocomplete-engine.js +75 -63
- package/src/components/autocomplete/check-autocomplete.js +3 -3
- package/src/components/dismiss-alerts.js +3 -3
- package/src/components/form-reset-submit.js +6 -6
- package/src/components/inner-nav/inner-nav.js +2 -2
- package/src/components/modal-alert/modal-alert.js +67 -65
- package/src/components/modal-alert/modal-alert.minimo.tokens.mjs +1 -1
- package/src/components/modal-popup/modal-popup.js +56 -56
- package/src/components/overlay/overlay.js +3 -3
- package/src/components/sf-macro/readme.md +1 -1
- package/src/components/sf-macro/sf-macro.js +27 -23
- package/src/components/slide-up-down-toggle/slide-up-down-toggle.js +83 -79
- package/src/components/slide-up-down-toggle/slide-up-down-toggle.module.css +44 -44
- package/src/components/snackbar/snackbar.js +19 -19
- package/src/components/spinner/spinner.js +3 -3
- package/src/components/unsplash-page/README.md +8 -8
- package/src/components/unsplash-page/fetch-unsplash-data.js +3 -3
- package/src/components/unsplash-page/unsplash-page.js +33 -33
- package/src/components/unsplash-page/unsplash-page.module.css +1 -1
- package/src/css/alerts.css +2 -2
- package/src/css/forms/forms.css +4 -4
- package/src/css/forms/validation.css +1 -1
- package/src/css/margin-padding-media.css +4 -4
- package/src/css/margin-padding.css +4 -4
- package/src/css/reset.css +11 -11
- package/src/icons/read-me.md +1 -1
- package/src/minimo.css +3 -3
- package/src/utilities/better-text-with-places.js +22 -22
- package/src/utilities/better-text.js +42 -35
- package/src/utilities/classnames.js +8 -8
- package/src/utilities/cookies.js +6 -6
- package/src/utilities/create-id.js +3 -3
- package/src/utilities/disable-btns-on-submit.js +2 -2
- package/src/utilities/dom-builder/dom-builder.js +89 -86
- package/src/utilities/dom-builder/domBuilderBasicSetup.js +9 -9
- package/src/utilities/dom-builder/parseDomString.js +23 -23
- package/src/utilities/dom-builder-helpers/README.md +2 -2
- package/src/utilities/dom-builder-helpers/form-tags.js +100 -100
- package/src/utilities/dom-builder-helpers/picture-tag.js +39 -39
- package/src/utilities/enable-submit-btns.js +1 -1
- package/src/utilities/escape-html.js +2 -2
- package/src/utilities/generate-pwd.js +4 -4
- package/src/utilities/linked-dates.js +13 -13
- package/src/utilities/parse-file-size.js +6 -6
- package/src/utilities/random-id.js +3 -3
- package/src/utilities/relative-date.js +40 -40
- package/src/utilities/sentence-case.js +1 -1
- package/src/utilities/strip-tags.js +1 -1
- package/src/utilities/title-case.js +1 -1
- package/src/utilities/truncate-string.js +5 -5
- package/src/utilities/validation-error-class.js +12 -12
- package/src/web-components/json-table/json-table-component.js +123 -121
- package/src/web-components/json-table/json-table-component.module.css +6 -6
- package/src/web-components/json-table/json-table.minimo.tokens.mjs +6 -6
- package/src/web-components/json-table/jt-docs/parametri.md +124 -0
- package/src/web-components/json-table/jt-docs.md +622 -0
- package/src/web-components/json-table/readme.md +3 -763
- package/src/web-components/json-table/src/cell-content.js +25 -25
- package/src/web-components/json-table/src/content-utils.js +21 -21
- package/src/web-components/json-table/src/data-types.js +32 -32
- package/src/web-components/json-table/src/defaults.js +230 -191
- package/src/web-components/json-table/src/get-data.js +55 -38
- package/src/web-components/json-table/src/icons.js +17 -17
- package/src/web-components/json-table/src/info-section.js +18 -18
- package/src/web-components/json-table/src/main-builder.js +45 -45
- package/src/web-components/json-table/src/pagination.js +32 -32
- package/src/web-components/json-table/src/parse-cols.js +18 -18
- package/src/web-components/json-table/src/parse-rows.js +17 -17
- package/src/web-components/json-table/src/resolve-params.js +53 -41
- package/src/web-components/json-table/src/search.js +13 -13
- package/src/web-components/json-table/src/sorting.js +20 -20
- package/src/web-components/json-table/src/table-body.js +13 -13
- package/src/web-components/json-table/src/table-builder.js +34 -34
- package/src/web-components/json-table/src/table-tfoot.js +8 -8
- package/src/web-components/json-table/src/table-thead.js +21 -21
- package/src/web-components/json-table/src/update-info.js +11 -11
- package/types/classes.d.ts +2 -2
- package/types/global.d.ts +18 -12
|
@@ -10,7 +10,7 @@ import warningIcon from './svg/warning.svg?inline';
|
|
|
10
10
|
import errorIcon from './svg/error.svg?inline';
|
|
11
11
|
import dangerIcon from './svg/danger.svg?inline';
|
|
12
12
|
|
|
13
|
-
//
|
|
13
|
+
// retrocompatibilità: i vecchi nomi di parametro snake_case sono mappati sul rispettivo equivalente camelCase
|
|
14
14
|
const legacyParamNames = {
|
|
15
15
|
extra_class: 'extraClass',
|
|
16
16
|
heading_class: 'headingClass',
|
|
@@ -31,7 +31,7 @@ const legacyParamNames = {
|
|
|
31
31
|
|
|
32
32
|
const defaults = {
|
|
33
33
|
|
|
34
|
-
//
|
|
34
|
+
// impostazioni applicate a tutti i tipi di dialog; sovrascrivibili per tipo
|
|
35
35
|
globals: {
|
|
36
36
|
extraClass: null,
|
|
37
37
|
onOpen: null,
|
|
@@ -49,7 +49,7 @@ const defaults = {
|
|
|
49
49
|
useAltIcon: false,
|
|
50
50
|
type: null,
|
|
51
51
|
|
|
52
|
-
//
|
|
52
|
+
// markup del pulsante extra, ignorato su confirm
|
|
53
53
|
extraBtn: null,
|
|
54
54
|
extraBtnFocus: true
|
|
55
55
|
},
|
|
@@ -76,7 +76,7 @@ const defaults = {
|
|
|
76
76
|
okBtnClass: 'btn btn-warning',
|
|
77
77
|
icon: warningIcon,
|
|
78
78
|
altIcon: dangerIcon,
|
|
79
|
-
useAltIcon: false //
|
|
79
|
+
useAltIcon: false // se true, usa altIcon (danger) invece dell'icona warning predefinita
|
|
80
80
|
},
|
|
81
81
|
info: {
|
|
82
82
|
type: 'info',
|
|
@@ -90,79 +90,79 @@ const defaults = {
|
|
|
90
90
|
title: 'Confermi?',
|
|
91
91
|
okBtnText: 'OK',
|
|
92
92
|
cancelBtnText: 'Annulla',
|
|
93
|
-
cancelBtnFocus: true, // false
|
|
93
|
+
cancelBtnFocus: true, // false per dare il focus al pulsante ok
|
|
94
94
|
okBtnClass: classnames('btn', styles.btnConfirm),
|
|
95
95
|
cancelBtnClass: classnames('btn', styles.btnConfirm, styles.btnHollow),
|
|
96
96
|
icon: confirmIcon,
|
|
97
97
|
altIcon: warningIcon,
|
|
98
|
-
useAltIcon: false //
|
|
98
|
+
useAltIcon: false // se true, usa altIcon (warning) invece dell'icona confirm predefinita
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
};
|
|
102
102
|
|
|
103
103
|
/**
|
|
104
104
|
* @typedef {Object} ModalAlertParams
|
|
105
|
-
* @property {string} [type] -
|
|
106
|
-
* @property {string | null} [title] -
|
|
107
|
-
* @property {string | null} [mes] -
|
|
108
|
-
* @property {((arg?: *) => void) | null} [callback] -
|
|
109
|
-
* @property {number | null} [timer] - timer
|
|
110
|
-
* @property {(() => void) | null} [onOpen] -
|
|
111
|
-
* @property {(() => void) | null} [onClose] -
|
|
112
|
-
* @property {string | null} [extraClass] -
|
|
113
|
-
* @property {string} [okBtnText] -
|
|
114
|
-
* @property {string} [okBtnClass] -
|
|
115
|
-
* @property {string} [cancelBtnText] -
|
|
116
|
-
* @property {string} [cancelBtnClass] -
|
|
117
|
-
* @property {boolean} [cancelBtnFocus] - true
|
|
118
|
-
* @property {string | null} [headingClass] -
|
|
119
|
-
* @property {DomBuilderItem | null} [extraBtn] -
|
|
120
|
-
* @property {boolean} [extraBtnFocus] - true
|
|
121
|
-
* @property {boolean} [animation] -
|
|
122
|
-
* @property {boolean} [showMarks] -
|
|
123
|
-
* @property {string | null} [textClass] -
|
|
124
|
-
* @property {string | null} [icon] -
|
|
125
|
-
* @property {string | null} [altIcon] -
|
|
126
|
-
* @property {boolean} [useAltIcon] -
|
|
105
|
+
* @property {string} [type] - Tipo di dialog: `success`, `error`, `warning`, `info` o `confirm`.
|
|
106
|
+
* @property {string | null} [title] - testo del titolo
|
|
107
|
+
* @property {string | null} [mes] - messaggio di testo (semplice o html) (default: null)
|
|
108
|
+
* @property {((arg?: *) => void) | null} [callback] - callback opzionale invocata dopo la scelta dell'utente (default: null)
|
|
109
|
+
* @property {number | null} [timer] - timer di chiusura automatica (ms). `null` per disattivarlo (default: null, tranne il tipo 'success' che ha 4000)
|
|
110
|
+
* @property {(() => void) | null} [onOpen] - callback opzionale all'apertura del dialog (default: null)
|
|
111
|
+
* @property {(() => void) | null} [onClose] - callback opzionale alla chiusura del dialog (default: null)
|
|
112
|
+
* @property {string | null} [extraClass] - classe extra opzionale del dialog (default: null)
|
|
113
|
+
* @property {string} [okBtnText] - testo del pulsante 'ok' (default: 'OK')
|
|
114
|
+
* @property {string} [okBtnClass] - classe alternativa per il pulsante 'ok'. Sostituisce quella predefinita
|
|
115
|
+
* @property {string} [cancelBtnText] - testo del pulsante 'cancel' (default: 'Annulla', solo tipo 'confirm')
|
|
116
|
+
* @property {string} [cancelBtnClass] - classe alternativa per il pulsante 'cancel'. Sostituisce quella predefinita
|
|
117
|
+
* @property {boolean} [cancelBtnFocus] - true se il pulsante cancel deve ricevere il focus all'apertura del dialog. Non ha effetto se è impostato un extraBtn (default: true, solo tipo 'confirm')
|
|
118
|
+
* @property {string | null} [headingClass] - classe extra opzionale per il titolo (default: null)
|
|
119
|
+
* @property {DomBuilderItem | null} [extraBtn] - pulsante extra opzionale (come oggetto domBuilder) (default: null)
|
|
120
|
+
* @property {boolean} [extraBtnFocus] - true se il pulsante extra deve ricevere il focus all'apertura del dialog (ha la precedenza sul focus assegnato al pulsante 'cancel') (default: true)
|
|
121
|
+
* @property {boolean} [animation] - se true, il dialog viene animato (default: true)
|
|
122
|
+
* @property {boolean} [showMarks] - se true, vengono mostrate le icone (default: true)
|
|
123
|
+
* @property {string | null} [textClass] - classe extra opzionale per il testo (default: null)
|
|
124
|
+
* @property {string | null} [icon] - Markup dell'icona usata per il dialog; di default è l'icona del tipo.
|
|
125
|
+
* @property {string | null} [altIcon] - Markup dell'icona alternativa, usata al posto di `icon` quando `useAltIcon` è `true`.
|
|
126
|
+
* @property {boolean} [useAltIcon] - Se `true`, usa `altIcon` al posto di `icon` (ad es. l'icona `danger` su un dialog `warning`, o l'icona `warning` su un dialog `confirm`) (default: false)
|
|
127
127
|
*/
|
|
128
128
|
|
|
129
129
|
/**
|
|
130
|
-
*
|
|
130
|
+
* Renderizza e apre un dialog modale di alert.
|
|
131
131
|
*
|
|
132
|
-
*
|
|
132
|
+
* Retrocompatibilità: i vecchi nomi di parametro snake_case (`extra_class`, `heading_class`,
|
|
133
133
|
* `text_class`, `extra_btn`, `extra_btn_selector`, `extra_btn_focus`, `ok_btn_text`,
|
|
134
134
|
* `ok_btn_class`, `cancel_btn_text`, `cancel_btn_class`, `cancel_focus`, `use_warning_icon`)
|
|
135
|
-
*
|
|
135
|
+
* sono ancora accettati e internamente mappati sul rispettivo equivalente camelCase, vedi `legacyParamNames`.
|
|
136
136
|
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* @param {object} [params] -
|
|
141
|
-
* @param {string} [params.type] -
|
|
142
|
-
* @param {string | null} [params.extraClass] -
|
|
143
|
-
* @param {(() => void) | null} [params.onOpen] -
|
|
144
|
-
* @param {(() => void) | null} [params.onClose] -
|
|
145
|
-
* @param {boolean} [params.animation] -
|
|
146
|
-
* @param {boolean} [params.showMarks] -
|
|
147
|
-
* @param {((arg?: *) => void) | null} [params.callback] -
|
|
148
|
-
* @param {number | null} [params.timer] - timer
|
|
149
|
-
* @param {string | null} [params.title] -
|
|
150
|
-
* @param {string | null} [params.mes] -
|
|
151
|
-
* @param {string | null} [params.headingClass] -
|
|
152
|
-
* @param {string | null} [params.textClass] -
|
|
153
|
-
* @param {string | null} [params.icon] -
|
|
154
|
-
* @param {string | null} [params.altIcon] -
|
|
155
|
-
* @param {boolean} [params.useAltIcon] -
|
|
156
|
-
* @param {DomBuilderItem | null} [params.extraBtn] -
|
|
157
|
-
* @param {boolean} [params.extraBtnFocus] - true
|
|
158
|
-
* @param {string} [params.okBtnText] -
|
|
159
|
-
* @param {string} [params.okBtnClass] -
|
|
160
|
-
* @param {string} [params.cancelBtnText] -
|
|
161
|
-
* @param {string} [params.cancelBtnClass] -
|
|
162
|
-
* @param {boolean} [params.cancelBtnFocus] - true
|
|
163
|
-
* @returns {Promise<string | boolean | undefined>}
|
|
164
|
-
*
|
|
165
|
-
* `confirm
|
|
137
|
+
* Il tipo base del parametro qui sotto è volutamente `object` (non `ModalAlertParams`): TypeScript
|
|
138
|
+
* espande le proprietà `@param` con notazione puntata (necessarie perché IntelliSense dell'editor elenchi
|
|
139
|
+
* ogni proprietà) solo quando il tipo base è il letterale `object`; un typedef con nome darebbe errore (TS8032).
|
|
140
|
+
* @param {object} [params] - Parametri del dialog (uniti ai default in base al tipo) (default: {})
|
|
141
|
+
* @param {string} [params.type] - Tipo di dialog: `success`, `error`, `warning`, `info` o `confirm`.
|
|
142
|
+
* @param {string | null} [params.extraClass] - classe extra opzionale del dialog (default: null)
|
|
143
|
+
* @param {(() => void) | null} [params.onOpen] - callback opzionale all'apertura del dialog (default: null)
|
|
144
|
+
* @param {(() => void) | null} [params.onClose] - callback opzionale alla chiusura del dialog (default: null)
|
|
145
|
+
* @param {boolean} [params.animation] - se true, il dialog viene animato (default: true)
|
|
146
|
+
* @param {boolean} [params.showMarks] - se true, vengono mostrate le icone (default: true)
|
|
147
|
+
* @param {((arg?: *) => void) | null} [params.callback] - callback opzionale invocata dopo la scelta dell'utente (default: null)
|
|
148
|
+
* @param {number | null} [params.timer] - timer di chiusura automatica (ms). `null` per disattivarlo (default: null, tranne il tipo 'success' che ha 4000)
|
|
149
|
+
* @param {string | null} [params.title] - testo del titolo
|
|
150
|
+
* @param {string | null} [params.mes] - messaggio di testo (semplice o html) (default: null)
|
|
151
|
+
* @param {string | null} [params.headingClass] - classe extra opzionale per il titolo (default: null)
|
|
152
|
+
* @param {string | null} [params.textClass] - classe extra opzionale per il testo (default: null)
|
|
153
|
+
* @param {string | null} [params.icon] - Markup dell'icona usata per il dialog; di default è l'icona del tipo.
|
|
154
|
+
* @param {string | null} [params.altIcon] - Markup dell'icona alternativa, usata al posto di `icon` quando `useAltIcon` è `true`.
|
|
155
|
+
* @param {boolean} [params.useAltIcon] - Se `true`, usa `altIcon` al posto di `icon` (ad es. l'icona `danger` su un dialog `warning`, o l'icona `warning` su un dialog `confirm`) (default: false)
|
|
156
|
+
* @param {DomBuilderItem | null} [params.extraBtn] - pulsante extra opzionale (come oggetto domBuilder) (default: null)
|
|
157
|
+
* @param {boolean} [params.extraBtnFocus] - true se il pulsante extra deve ricevere il focus all'apertura del dialog (ha la precedenza sul focus assegnato al pulsante 'cancel') (default: true)
|
|
158
|
+
* @param {string} [params.okBtnText] - testo del pulsante 'ok' (default: 'OK')
|
|
159
|
+
* @param {string} [params.okBtnClass] - classe alternativa per il pulsante 'ok'. Sostituisce quella predefinita
|
|
160
|
+
* @param {string} [params.cancelBtnText] - testo del pulsante 'cancel' (default: 'Annulla', solo tipo 'confirm')
|
|
161
|
+
* @param {string} [params.cancelBtnClass] - classe alternativa per il pulsante 'cancel'. Sostituisce quella predefinita
|
|
162
|
+
* @param {boolean} [params.cancelBtnFocus] - true se il pulsante cancel deve ricevere il focus all'apertura del dialog. Non ha effetto se è impostato un extraBtn (default: true, solo tipo 'confirm')
|
|
163
|
+
* @returns {Promise<string | boolean | undefined>} Si risolve alla chiusura del dialog. Se il
|
|
164
|
+
* pulsante che lo chiude ha un attributo `data-malert-result`, viene usata quella stringa; altrimenti, per i
|
|
165
|
+
* dialog `confirm`, si risolve con `true`/`false` per il pulsante OK/Annulla; altrimenti con `undefined`.
|
|
166
166
|
*/
|
|
167
167
|
export function modalAlert(params = {}) {
|
|
168
168
|
|
|
@@ -173,7 +173,7 @@ export function modalAlert(params = {}) {
|
|
|
173
173
|
// garbage collection
|
|
174
174
|
document.querySelector('.modal-alert')?.remove();
|
|
175
175
|
|
|
176
|
-
//
|
|
176
|
+
// retrocompatibilità: mappa le vecchie chiavi snake_case sul rispettivo equivalente camelCase
|
|
177
177
|
const rawParams = /** @type {Record<string, any>} */ (params);
|
|
178
178
|
Object.entries(legacyParamNames).forEach(([legacyKey, newKey]) => {
|
|
179
179
|
if(legacyKey in rawParams && !(newKey in rawParams)) {
|
|
@@ -189,7 +189,7 @@ export function modalAlert(params = {}) {
|
|
|
189
189
|
...params
|
|
190
190
|
});
|
|
191
191
|
|
|
192
|
-
//
|
|
192
|
+
// retrocompatibilità: 'danger' è un alias di 'error'
|
|
193
193
|
if(params.type === 'danger') {
|
|
194
194
|
params.type = 'error';
|
|
195
195
|
}
|
|
@@ -325,9 +325,11 @@ export function modalAlert(params = {}) {
|
|
|
325
325
|
params.onClose();
|
|
326
326
|
}
|
|
327
327
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
328
|
+
/*
|
|
329
|
+
Se presente, l'argomento risolto/della callback è `btn.dataset.malertResult`
|
|
330
|
+
Altrimenti, se il tipo di modale è `confirm`, è `true` per il pulsante OK, `false` per il pulsante Annulla
|
|
331
|
+
Altrimenti, per tutti gli altri tipi di modale, è `undefined`
|
|
332
|
+
*/
|
|
331
333
|
let arg;
|
|
332
334
|
if(btn) {
|
|
333
335
|
if(btn.dataset?.malertResult) {
|
|
@@ -8,7 +8,7 @@ import * as styles from './modal-popup.module.css';
|
|
|
8
8
|
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
// TODO
|
|
11
|
+
// TODO testare la modalità iframe
|
|
12
12
|
|
|
13
13
|
/** @type {HTMLDialogElement | undefined} */
|
|
14
14
|
let dialogEl;
|
|
@@ -28,8 +28,8 @@ function closeDialog(el) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
31
|
+
* Racchiude in un array un singolo oggetto domBuilder (non un array, non un Node DOM),
|
|
32
|
+
* così che `content`/`headerContent`/`footerContent` possano essere gestiti in modo uniforme con gli array domBuilder.
|
|
33
33
|
* @param {*} value
|
|
34
34
|
* @returns {*}
|
|
35
35
|
*/
|
|
@@ -41,7 +41,7 @@ function normalizeContentParam(value) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
|
-
* @param {*} value -
|
|
44
|
+
* @param {*} value - un valore non nullo di `content`/`headerContent`/`footerContent`
|
|
45
45
|
* @returns {'domBuilder' | 'node' | 'function' | 'html'}
|
|
46
46
|
*/
|
|
47
47
|
function getContentKind(value) {
|
|
@@ -58,9 +58,9 @@ function getContentKind(value) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
61
|
+
* Risolve le proprietà domBuilder `content`/`children` per un valore di `headerContent`/`footerContent`.
|
|
62
|
+
* Il tipo `function` non viene gestito qui: viene invocato separatamente, dalla `callback` dell'elemento,
|
|
63
|
+
* una volta che l'elemento contenitore esiste.
|
|
64
64
|
* @param {*} value
|
|
65
65
|
* @returns {{content: string | Node | null, children: DomBuilderItem[] | undefined}}
|
|
66
66
|
*/
|
|
@@ -77,50 +77,50 @@ function getDomBuilderContentProps(value) {
|
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
79
|
* modalPopup
|
|
80
|
-
*
|
|
80
|
+
* Apre un dialog popup con il contenuto indicato.
|
|
81
81
|
*
|
|
82
|
-
*
|
|
83
|
-
* -
|
|
84
|
-
* -
|
|
85
|
-
* -
|
|
82
|
+
* Il contenuto può essere fornito come:
|
|
83
|
+
* - un array domBuilder, testo semplice o HTML (tramite `content`)
|
|
84
|
+
* - contenuto caricato via Ajax (tramite `ajaxUrl` e `ajaxCallback`)
|
|
85
|
+
* - un iframe (tramite `iframeUrl`)
|
|
86
86
|
*
|
|
87
|
-
*
|
|
87
|
+
* Quando più opzioni sono in conflitto, ha la precedenza la prima applicabile nell'elenco qui sopra.
|
|
88
88
|
*
|
|
89
89
|
* @param {Object} params
|
|
90
|
-
* @param {string | null} [params.dialogExtraClassName=null] -
|
|
91
|
-
* @param {string | DomBuilderItem | DomBuilderItem[] | Node | ((container: HTMLElement) => void) | null} [params.content=null] -
|
|
92
|
-
* @param {string | null} [params.contentExtraClassName=null] -
|
|
93
|
-
* @param {HTMLElement | null} [params.triggerElement=null] -
|
|
94
|
-
* @param {boolean} [params.addFocus=true] -
|
|
95
|
-
* @param {string | null} [params.headerExtraClassName=null] -
|
|
96
|
-
* @param {string | null} [params.footerExtraClassName=null] -
|
|
97
|
-
* @param {string | number | null} [params.dialogWidth=null] -
|
|
98
|
-
* @param {string | number | null} [params.dialogMinWidth=null] -
|
|
99
|
-
* @param {string | number | null} [params.dialogMaxWidth=null] -
|
|
100
|
-
* @param {string | number | null} [params.dialogHeight=null] -
|
|
101
|
-
* @param {string | number | null} [params.dialogMinHeight=null] -
|
|
102
|
-
* @param {string | number | null} [params.dialogMaxHeight=null] -
|
|
103
|
-
* @param {string | number | null} [params.dialogContentPadding=null] -
|
|
104
|
-
* @param {string | null} [params.iframeUrl=null] - URL
|
|
105
|
-
* @param {string | null} [params.ajaxUrl=null] - URL
|
|
106
|
-
* @param {((el: HTMLDialogElement) => void) | null} [params.openCallback=null] -
|
|
107
|
-
* @param {((el: HTMLDialogElement) => void) | null} [params.closeCallback=null] -
|
|
108
|
-
* @param {((data: *, el: Element) => void) | null} [params.ajaxCallback=null] -
|
|
109
|
-
* @param {boolean} [params.addScrollbarPadding=false] -
|
|
110
|
-
* @param {string | DomBuilderItem | DomBuilderItem[] | Node | ((container: HTMLElement) => void) | null} [params.headerContent=null] -
|
|
111
|
-
* @param {string | DomBuilderItem | DomBuilderItem[] | Node | ((container: HTMLElement) => void) | null} [params.footerContent=null] -
|
|
112
|
-
* @returns {HTMLDialogElement}
|
|
90
|
+
* @param {string | null} [params.dialogExtraClassName=null] - Classe extra aggiunta all'elemento dialog (default: null)
|
|
91
|
+
* @param {string | DomBuilderItem | DomBuilderItem[] | Node | ((container: HTMLElement) => void) | null} [params.content=null] - Testo semplice, HTML, un oggetto domBuilder, un array domBuilder, un nodo DOM (aggiunto così com'è) o una funzione invocata con l'elemento contenitore del contenuto per popolarlo direttamente (`content(dialogContentEl)`) (default: null)
|
|
92
|
+
* @param {string | null} [params.contentExtraClassName=null] - Classe extra aggiunta al wrapper del contenuto (default: null)
|
|
93
|
+
* @param {HTMLElement | null} [params.triggerElement=null] - Elemento opzionale che ha attivato il popup; se impostato, su di esso vengono gestiti `aria-haspopup`, `aria-controls` e `aria-expanded` (default: null)
|
|
94
|
+
* @param {boolean} [params.addFocus=true] - se true, il dialog riceve il focus dopo l'apertura (default: true)
|
|
95
|
+
* @param {string | null} [params.headerExtraClassName=null] - Classe extra aggiunta al wrapper dell'header (default: null)
|
|
96
|
+
* @param {string | null} [params.footerExtraClassName=null] - Classe extra aggiunta al wrapper del footer (default: null)
|
|
97
|
+
* @param {string | number | null} [params.dialogWidth=null] - valore opzionale della larghezza del dialog - se impostato, sovrascrive la custom prop css principale (default: null)
|
|
98
|
+
* @param {string | number | null} [params.dialogMinWidth=null] - valore opzionale della larghezza minima del dialog - se impostato, sovrascrive la custom prop css principale (default: null)
|
|
99
|
+
* @param {string | number | null} [params.dialogMaxWidth=null] - valore opzionale della larghezza massima del dialog - se impostato, sovrascrive la custom prop css principale (default: null)
|
|
100
|
+
* @param {string | number | null} [params.dialogHeight=null] - valore opzionale dell'altezza del dialog - se impostato, sovrascrive la custom prop css principale (default: null)
|
|
101
|
+
* @param {string | number | null} [params.dialogMinHeight=null] - valore opzionale dell'altezza minima del dialog - se impostato, sovrascrive la custom prop css principale (default: null)
|
|
102
|
+
* @param {string | number | null} [params.dialogMaxHeight=null] - valore opzionale dell'altezza massima del dialog - se impostato, sovrascrive la custom prop css principale (default: null)
|
|
103
|
+
* @param {string | number | null} [params.dialogContentPadding=null] - valore opzionale del padding del contenuto del dialog - se impostato, sovrascrive la custom prop css principale (default: null)
|
|
104
|
+
* @param {string | null} [params.iframeUrl=null] - URL da caricare in un iframe (default: null)
|
|
105
|
+
* @param {string | null} [params.ajaxUrl=null] - URL per il caricamento del contenuto via Ajax (default: null)
|
|
106
|
+
* @param {((el: HTMLDialogElement) => void) | null} [params.openCallback=null] - Chiamata dopo l'apertura, riceve l'elemento dialog (`openCallback(dialogEl)`) (default: null)
|
|
107
|
+
* @param {((el: HTMLDialogElement) => void) | null} [params.closeCallback=null] - Chiamata subito prima della rimozione del dialog, riceve l'elemento dialog (`closeCallback(dialogEl)`). NB: `dialogEl` viene rimosso subito dopo la chiusura (default: null)
|
|
108
|
+
* @param {((data: *, el: Element) => void) | null} [params.ajaxCallback=null] - Chiamata con la risposta Ajax e l'elemento del contenuto (default: null)
|
|
109
|
+
* @param {boolean} [params.addScrollbarPadding=false] - Aggiunge padding destro per compensare la scrollbar (default: false)
|
|
110
|
+
* @param {string | DomBuilderItem | DomBuilderItem[] | Node | ((container: HTMLElement) => void) | null} [params.headerContent=null] - Contenuto dell'header: testo semplice, HTML, un oggetto domBuilder, un array domBuilder, un nodo DOM o una funzione invocata con l'elemento contenitore dell'header per popolarlo direttamente (default: null)
|
|
111
|
+
* @param {string | DomBuilderItem | DomBuilderItem[] | Node | ((container: HTMLElement) => void) | null} [params.footerContent=null] - Contenuto del footer: stessi tipi accettati di `headerContent`, invocata con l'elemento contenitore del footer (default: null)
|
|
112
|
+
* @returns {HTMLDialogElement} L'elemento dialog.
|
|
113
113
|
*/
|
|
114
114
|
|
|
115
115
|
export function modalPopup({
|
|
116
116
|
|
|
117
|
-
/**
|
|
117
|
+
/** classe extra del dialog */
|
|
118
118
|
dialogExtraClassName = null,
|
|
119
119
|
|
|
120
|
-
/** extra
|
|
120
|
+
/** classe extra aggiunta a dialogInner */
|
|
121
121
|
contentExtraClassName = null,
|
|
122
122
|
|
|
123
|
-
/**
|
|
123
|
+
/** valori opzionali per sovrascrivere le custom props principali */
|
|
124
124
|
dialogWidth = null,
|
|
125
125
|
dialogMinWidth = null,
|
|
126
126
|
dialogMaxWidth = null,
|
|
@@ -131,14 +131,14 @@ export function modalPopup({
|
|
|
131
131
|
|
|
132
132
|
addFocus = true,
|
|
133
133
|
|
|
134
|
-
/** iframe
|
|
134
|
+
/** url dell'iframe */
|
|
135
135
|
iframeUrl = null,
|
|
136
136
|
|
|
137
|
-
/** content:
|
|
137
|
+
/** content: testo semplice, html, oggetto/array domBuilder, nodo DOM o function(container) */
|
|
138
138
|
content = null,
|
|
139
139
|
|
|
140
|
-
/**
|
|
141
|
-
*
|
|
140
|
+
/** url e callback Ajax.
|
|
141
|
+
* La callback viene invocata con i dati della risposta e l'elemento contenitore del contenuto
|
|
142
142
|
* (`ajaxCallback(data, dialogContentEl)`)
|
|
143
143
|
*/
|
|
144
144
|
ajaxUrl = null,
|
|
@@ -146,21 +146,21 @@ export function modalPopup({
|
|
|
146
146
|
|
|
147
147
|
openCallback = null,
|
|
148
148
|
closeCallback = null,
|
|
149
|
-
addScrollbarPadding = false, //
|
|
149
|
+
addScrollbarPadding = false, // aggiunge padding destro extra per compensare la scrollbar
|
|
150
150
|
|
|
151
|
-
/** header
|
|
151
|
+
/** contenuto dell'header: testo semplice, HTML, oggetto/array domBuilder, nodo DOM o function(container) */
|
|
152
152
|
headerContent = null,
|
|
153
153
|
|
|
154
|
-
/** extra
|
|
154
|
+
/** classe extra aggiunta all'header */
|
|
155
155
|
headerExtraClassName = null,
|
|
156
156
|
|
|
157
|
-
/** footer
|
|
157
|
+
/** contenuto del footer: testo semplice, HTML, oggetto/array domBuilder, nodo DOM o function(container) */
|
|
158
158
|
footerContent = null,
|
|
159
159
|
|
|
160
|
-
/** extra
|
|
160
|
+
/** classe extra aggiunta al footer */
|
|
161
161
|
footerExtraClassName = null,
|
|
162
162
|
|
|
163
|
-
/**
|
|
163
|
+
/** elemento che ha attivato il popup: se impostato, su di esso vengono gestiti `aria-haspopup`, `aria-controls` e `aria-expanded` */
|
|
164
164
|
triggerElement = null,
|
|
165
165
|
|
|
166
166
|
}) {
|
|
@@ -170,7 +170,7 @@ export function modalPopup({
|
|
|
170
170
|
throw '[modalContent] parametri `content`, `ajaxUrl`/ `ajaxCallback` e `iframeUrl` mancanti';
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
//
|
|
173
|
+
// consente un singolo oggetto domBuilder (oltre a un array domBuilder) racchiudendolo in un array
|
|
174
174
|
content = normalizeContentParam(content);
|
|
175
175
|
headerContent = normalizeContentParam(headerContent);
|
|
176
176
|
footerContent = normalizeContentParam(footerContent);
|
|
@@ -188,10 +188,10 @@ export function modalPopup({
|
|
|
188
188
|
|
|
189
189
|
|
|
190
190
|
|
|
191
|
-
//
|
|
191
|
+
// catturato per l'uso nella closure della callback ajax (ajaxUrl non è null quando mode === 'ajax')
|
|
192
192
|
const safeAjaxUrl = /** @type {string} */ (ajaxUrl);
|
|
193
193
|
|
|
194
|
-
// fine
|
|
194
|
+
// regolazione fine delle custom props
|
|
195
195
|
/** @type {Record<string, string | number | null>} */
|
|
196
196
|
const cpropsValues = {
|
|
197
197
|
dialogWidth,
|
|
@@ -221,7 +221,7 @@ export function modalPopup({
|
|
|
221
221
|
}
|
|
222
222
|
});
|
|
223
223
|
|
|
224
|
-
//
|
|
224
|
+
// id per aria-controls (trigger → dialog) e aria-labelledby (dialog → header), generati solo quando servono
|
|
225
225
|
const dialogId = triggerElement != null ? `mpopup-${crypto.randomUUID()}` : null;
|
|
226
226
|
const headerId = headerContent != null ? `mpopup-header-${crypto.randomUUID()}` : null;
|
|
227
227
|
|
|
@@ -322,19 +322,19 @@ export function modalPopup({
|
|
|
322
322
|
}
|
|
323
323
|
], document.body);
|
|
324
324
|
|
|
325
|
-
// snapshot: dialogEl
|
|
325
|
+
// snapshot: dialogEl potrebbe essere sovrascritto da una successiva chiamata a modalPopup
|
|
326
326
|
const thisDialog = /** @type {HTMLDialogElement} */ (dialogEl);
|
|
327
327
|
|
|
328
328
|
if(openCallback) {
|
|
329
329
|
openCallback(thisDialog);
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
//
|
|
332
|
+
// listener del pulsante di chiusura
|
|
333
333
|
thisDialog.querySelector(`.${styles.closeButton}`)?.addEventListener('click', () => {
|
|
334
334
|
closeDialog(thisDialog);
|
|
335
335
|
}, false);
|
|
336
336
|
|
|
337
|
-
// // Esc
|
|
337
|
+
// // Esc e click sul backdrop
|
|
338
338
|
// thisDialog.addEventListener('cancel', (e) => {
|
|
339
339
|
// e.preventDefault();
|
|
340
340
|
// closeDialog(thisDialog);
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import './overlay.css';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Aggiunge un `div` overlay all'elemento di contesto indicato.
|
|
6
6
|
* @param {Element} [context=document.body] (default: document.body)
|
|
7
|
-
* @param {boolean} [scroll_lock=false] -
|
|
7
|
+
* @param {boolean} [scroll_lock=false] - Se true, blocca lo scroll del body tramite `overflow-hidden` (default: false)
|
|
8
8
|
* @returns {void}
|
|
9
9
|
*/
|
|
10
10
|
export function overlay(context = document.body, scroll_lock = false) {
|
|
@@ -15,7 +15,7 @@ export function overlay(context = document.body, scroll_lock = false) {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* Rimuove l'elemento overlay dal contesto indicato e riabilita lo scroll del body.
|
|
19
19
|
* @param {Element} [context=document.body] (default: document.body)
|
|
20
20
|
* @returns {void}
|
|
21
21
|
*/
|
|
@@ -8,25 +8,27 @@ import './sf-macro.css';
|
|
|
8
8
|
// di sf-macro.css qui sopra (questione lasciata in sospeso).
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Inizializza i campi collection macro di Symfony (fieldset ripetibili).
|
|
12
12
|
* @param {Object} [options={}] (default: {})
|
|
13
|
-
* @param {string} [options.wrapper_selector='.sf-macro-wrapper'] -
|
|
14
|
-
* @param {string} [options.row_selector='.sf-macro-riga'] -
|
|
15
|
-
* @param {string} [options.closeBtnClass='sf-macro-close-btn'] -
|
|
16
|
-
* @param {string} [options.addBtnClass='sf-macro-riga-add'] -
|
|
17
|
-
* @param {string} [options.containerClass='sf-macro-container'] -
|
|
18
|
-
* @param {((newRow: Element | null, addBtn: Element | null) => void) | null} [options.add_callback=null] -
|
|
19
|
-
* @param {((row: Element | null, closeBtn: Element | null) => boolean | Promise<boolean>) | null} [options.preDelCallback=null] -
|
|
20
|
-
* @param {((row: Element | null, closeBtn: Element | null) => void) | null} [options.del_callback=null] -
|
|
21
|
-
* @param {boolean} [options.insertAtTop=false] -
|
|
13
|
+
* @param {string} [options.wrapper_selector='.sf-macro-wrapper'] - Selettore dell'elemento wrapper esterno. (default: '.sf-macro-wrapper')
|
|
14
|
+
* @param {string} [options.row_selector='.sf-macro-riga'] - Selettore di ogni riga ripetibile. (default: '.sf-macro-riga')
|
|
15
|
+
* @param {string} [options.closeBtnClass='sf-macro-close-btn'] - Nome della classe del pulsante di rimozione riga. (default: 'sf-macro-close-btn')
|
|
16
|
+
* @param {string} [options.addBtnClass='sf-macro-riga-add'] - Nome della classe del pulsante di aggiunta riga. (default: 'sf-macro-riga-add')
|
|
17
|
+
* @param {string} [options.containerClass='sf-macro-container'] - Nome della classe del contenitore delle righe. (default: 'sf-macro-container')
|
|
18
|
+
* @param {((newRow: Element | null, addBtn: Element | null) => void) | null} [options.add_callback=null] - Chiamata dopo l'aggiunta di una riga; riceve la nuova riga e il pulsante di aggiunta. (default: null)
|
|
19
|
+
* @param {((row: Element | null, closeBtn: Element | null) => boolean | Promise<boolean>) | null} [options.preDelCallback=null] - Chiamata alla pressione del pulsante di rimozione riga, prima che la riga venga rimossa; riceve la riga e il pulsante di chiusura. Può restituire una Promise (ad es. per attendere una finestra di conferma): la riga viene rimossa solo se il valore risolto non è `false`. (default: null)
|
|
20
|
+
* @param {((row: Element | null, closeBtn: Element | null) => void) | null} [options.del_callback=null] - Chiamata dopo la rimozione di una riga; riceve la riga rimossa (ormai scollegata) e il pulsante di chiusura. (default: null)
|
|
21
|
+
* @param {boolean} [options.insertAtTop=false] - Se true, le nuove righe vengono inserite in cima. (default: false)
|
|
22
22
|
* @returns {void}
|
|
23
23
|
*/
|
|
24
24
|
export function sf_macro({
|
|
25
25
|
wrapper_selector = '.sf-macro-wrapper',
|
|
26
26
|
row_selector = '.sf-macro-riga',
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
/*
|
|
28
|
+
Modificare closeBtnClass/addBtnClass/containerClass solo se non si vuole usare
|
|
29
|
+
il file sf-macro.css predefinito: le classi predefinite inutilizzate non vengono rimosse da questo
|
|
30
|
+
componente, la pulizia è delegata a valle a PurgeCSS (o equivalente).
|
|
31
|
+
*/
|
|
30
32
|
closeBtnClass = 'sf-macro-close-btn',
|
|
31
33
|
addBtnClass = 'sf-macro-riga-add',
|
|
32
34
|
containerClass = 'sf-macro-container',
|
|
@@ -36,13 +38,15 @@ export function sf_macro({
|
|
|
36
38
|
insertAtTop = false
|
|
37
39
|
}={}) {
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
/*
|
|
42
|
+
Singolo listener delegato: risolve al momento del click l'antenato wrapper_selector
|
|
43
|
+
più vicino al target del click, invece di collegare un listener per ogni
|
|
44
|
+
wrapper trovato in fase di init. È necessario perché wrapper_selector possa essere
|
|
45
|
+
condiviso da istanze di wrapper annidate (vince sempre la più vicina, senza
|
|
46
|
+
propagazione a quelle esterne) e perché funzioni sui wrapper creati dinamicamente
|
|
47
|
+
dopo questa chiamata (ad es. un wrapper annidato in una riga appena aggiunta), che
|
|
48
|
+
altrimenti non riceverebbero mai un proprio listener.
|
|
49
|
+
*/
|
|
46
50
|
document.addEventListener('click', e => {
|
|
47
51
|
const target = /** @type {HTMLElement} */ (e.target);
|
|
48
52
|
|
|
@@ -89,9 +93,9 @@ export function sf_macro({
|
|
|
89
93
|
if(preDelCallback && typeof preDelCallback === 'function') {
|
|
90
94
|
const pre_del_result = preDelCallback(riga, action_btn);
|
|
91
95
|
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
//
|
|
96
|
+
// Supporta sia un preDelCallback sincrono che restituisce un booleano sia uno asincrono
|
|
97
|
+
// (che restituisce una Promise, ad es. in attesa di una finestra di conferma) senza richiedere
|
|
98
|
+
// che il chiamante gestisca da sé la rimozione della riga.
|
|
95
99
|
if(pre_del_result instanceof Promise) {
|
|
96
100
|
pre_del_result.then(ok => {
|
|
97
101
|
if(ok !== false) remove_row();
|