@massimo-cassandro/minimo 1.4.11 → 1.4.13

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
@@ -6,11 +6,21 @@ My light framework + utilities
6
6
 
7
7
  ```bash
8
8
  npm i @massimo-cassandro/minimo
9
+
10
+ # optional:
11
+ npm i -D @massimo-cassandro/eslint-config
12
+ npm i -D @massimo-cassandro/stylelint-config
9
13
  ```
10
14
 
11
15
  > 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.
12
16
  >
13
- > For a complete install of all required modules, use the install script at `./webpack-setup/starter-install.sh`.
17
+
18
+ For a complete install of all required webpack modules, use the install script at `./webpack-setup/starter-install.sh` launching:
19
+
20
+ ```bash
21
+ zsh ./node_modules/@massimo-cassandro/minimo/webpack-setup/starter-install.sh
22
+ ```
23
+
14
24
 
15
25
  ---
16
26
 
@@ -0,0 +1,32 @@
1
+ // -----------------------------------------------------------------------
2
+ // Generated file — do not edit manually.
3
+ // This file is produced by the design-token build pipeline.
4
+ // Source of truth: the token files in the project source directory.
5
+ // Re-generate with: node build-tokens.mjs --config <path/to/config>
6
+ // -----------------------------------------------------------------------
7
+ {
8
+ "switch": {
9
+ "background": {
10
+ "color": {
11
+ "$type": "color",
12
+ "$value": "{neutral.100}"
13
+ }
14
+ },
15
+ "cursor": {
16
+ "background": {
17
+ "color": {
18
+ "$type": "color",
19
+ "$value": "{body.background.color}"
20
+ }
21
+ }
22
+ },
23
+ "checked": {
24
+ "background": {
25
+ "color": {
26
+ "$type": "color",
27
+ "$value": "{primary.100}"
28
+ }
29
+ }
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,29 @@
1
+ export default {
2
+ switch: {
3
+ background: {
4
+ color: {
5
+ $value: '{neutral.100}',
6
+ $type: 'color'
7
+ }
8
+ },
9
+
10
+ cursor: {
11
+ background: {
12
+ color: {
13
+ $value: '{body.background.color}',
14
+ $type: 'color'
15
+ }
16
+ },
17
+ },
18
+
19
+ checked: {
20
+ background: {
21
+ color: {
22
+ $value: '{primary.100}',
23
+ $type: 'color'
24
+ }
25
+ },
26
+ }
27
+
28
+ }
29
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massimo-cassandro/minimo",
3
- "version": "1.4.11",
3
+ "version": "1.4.13",
4
4
  "description": "Light css framework & utilities",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/massimo-cassandro/minimo#readme",
@@ -44,8 +44,9 @@
44
44
  }
45
45
  },
46
46
  "devDependencies": {
47
- "@massimo-cassandro/dev-updater": "^3.0.4",
48
- "@massimo-cassandro/eslint-config": "^3.0.7",
49
- "@massimo-cassandro/stylelint-config": "^2.3.7"
47
+ "@massimo-cassandro/dev-updater": "^3.0.6",
48
+ "@massimo-cassandro/eslint-config": "^3.0.9",
49
+ "@massimo-cassandro/stylelint-config": "^2.3.9",
50
+ "style-dictionary": "^5.4.4"
50
51
  }
51
- }
52
+ }
@@ -0,0 +1,152 @@
1
+ import { domBuilder, classnames } from '../index.js';
2
+ import { spinner } from '@minimo/spinner.js';
3
+ import * as styles from './modal-content.module.css?inline';
4
+
5
+
6
+
7
+
8
+ // https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement
9
+
10
+ export function modalContent({
11
+
12
+ /** iframe | content */
13
+ mode = 'iframe',
14
+
15
+ /** dialog extra class */
16
+ dialogClassName = null,
17
+
18
+ /** iframe url */
19
+ iframeUrl = null,
20
+
21
+ /** content string (markup) o array */
22
+ content = null,
23
+
24
+ /** content extra classname */
25
+ contentClassName = null,
26
+
27
+ /** dimensione modal: full, large, medium o small */
28
+ size = 'large',
29
+
30
+ /** padding: std (var(--size-md) 0) o none */
31
+ padding = 'std'
32
+ }) {
33
+
34
+ if(!['content', 'iframe'].includes(mode)) {
35
+ throw `modalContent: parametro 'mode' non corretto (${mode})`;
36
+ }
37
+ if(!iframeUrl && mode === 'iframe') {
38
+ throw 'modalContent: parametro `iframeUrl` mancante';
39
+ }
40
+ if(content == null && mode === 'content') {
41
+ throw 'modalContent: parametro `content` mancante';
42
+ }
43
+ if(!['full', 'large', 'medium', 'small'].includes(size)) {
44
+ throw `modalContent: parametro 'size' non corretto (${size})`;
45
+ }
46
+ if(!['std', 'none'].includes(padding)) {
47
+ throw `modalContent: parametro 'padding' non corretto (${padding})`;
48
+ }
49
+
50
+ let dialog_element, dialog_inner, iframe_element = null, content_element = null;
51
+ const iframeLoadEventHandler = () => {
52
+ dialog_element.classList.add(styles.on);
53
+ };
54
+
55
+ if(!dialog_element) {
56
+
57
+ domBuilder([
58
+ {
59
+ tag: 'dialog',
60
+ className: styles.dialog,
61
+ attrs: {
62
+ 'closedby': 'any'
63
+ },
64
+ children: [
65
+ {
66
+ tag: 'button',
67
+ className: styles.closeButton, // TODO standardizzare close btn
68
+ attrs: {
69
+ 'aria-label': 'Chiudi'
70
+ }
71
+ },
72
+ {
73
+ className: styles.dialogInner,
74
+ children: [
75
+ {
76
+ className: styles.modalSpinner,
77
+ content: spinner({container: null, withOverlay: false })
78
+ }
79
+
80
+ ]
81
+ }
82
+ ]
83
+ }
84
+ ], document.body);
85
+
86
+
87
+ dialog_element = document.querySelector(`.${styles.dialog}`);
88
+ dialog_inner = dialog_element.querySelector(`.${styles.dialogInner}`);
89
+
90
+ dialog_element.querySelector(`.${styles.closeButton}`).addEventListener('click', () => {
91
+ dialog_element.close();
92
+ }, false);
93
+
94
+ // listener chiusura
95
+ dialog_element.addEventListener('close', () => {
96
+
97
+ iframe_element?.removeEventListener('load', iframeLoadEventHandler, false);
98
+ iframe_element?.remove();
99
+ content_element?.remove();
100
+
101
+ document.body.classList.remove('overflow-hidden');
102
+ dialog_element.classList.remove(styles.on);
103
+ }, false);
104
+
105
+ }
106
+
107
+
108
+ // aggiunta classi extra
109
+ dialog_element.className = classnames(
110
+ styles.dialog,
111
+ dialogClassName,
112
+ styles[`size-${size}`],
113
+ styles[`padding-${padding}`]
114
+ );
115
+
116
+ domBuilder([
117
+ {
118
+ condition: mode === 'iframe',
119
+ className: styles.iframeEl,
120
+ tag: 'iframe',
121
+ attrs: {
122
+ src: ''
123
+ }
124
+ },
125
+ {
126
+ condition: mode === 'content',
127
+ className: classnames(styles.contentEl, contentClassName),
128
+ content: (content && !Array.isArray(content))? content : null,
129
+ children: (content && Array.isArray(content))? content : null
130
+ }
131
+ ], dialog_inner);
132
+
133
+
134
+ document.body.classList.add('overflow-hidden');
135
+ dialog_element.showModal();
136
+ dialog_element.focus();
137
+
138
+
139
+ if(mode === 'iframe') {
140
+ iframe_element = dialog_element.querySelector(`.${styles.iframeEl}`);
141
+ iframe_element.src = iframeUrl;
142
+
143
+ // listener load per i frame
144
+ iframe_element?.addEventListener('load', iframeLoadEventHandler, false);
145
+
146
+
147
+ } else if (mode === 'content') {
148
+ content_element = dialog_element.querySelector(`.${styles.contentEl}`);
149
+ dialog_element.classList.add(styles.on);
150
+ }
151
+
152
+ }
@@ -0,0 +1,142 @@
1
+ /* TODO custom props nel file generale */
2
+ /* TODO prevedere opzione con bordo del pulsante di chiusura (NB: si disallinea) */
3
+ /* TODO cambiare icona chiusura con svg? */
4
+ /* TODO aggiungere wrapper intorno a dialogInner (a filo con il dialog) per la scroll bar */
5
+
6
+ .dialog { /* dialog */
7
+ --modal-border-radius: var(--radius-md, 1.2rem);
8
+ position: fixed;
9
+ max-width: 80dvw;
10
+ max-height: 90dvh;
11
+ padding: 0;
12
+ margin: auto;
13
+ overflow: visible;
14
+ background-color: var(--body-bg-color, #fff);
15
+ border: none;
16
+ border-radius: var(--modal-border-radius);
17
+ box-shadow: 0 0 2rem .5rem rgb(0 0 0 / .5);
18
+ scale: 0;
19
+ transition: scale .2s ease-out;
20
+
21
+ /* stylelint-disable selector-class-pattern */
22
+ &.size-full {
23
+ width: 80dvw;
24
+ height: 90dvh;
25
+ }
26
+ /* dimensioni std -> size == 'large' */
27
+ &.size-large {
28
+ width: 50rem; /* 800px */
29
+ height: auto;
30
+ }
31
+ &.size-medium {
32
+ width: 37.5rem; /* 600px */
33
+ height: 37.5rem;
34
+ }
35
+ &.size-small {
36
+ width: 32rem; /* ~500 x 400 */
37
+ height: 25rem;
38
+ }
39
+ /* stylelint-enable selector-class-pattern */
40
+
41
+ &.on {
42
+ scale: 1;
43
+ }
44
+
45
+ &::backdrop {
46
+ overscroll-behavior: contain;
47
+ background-color: rgb(0 0 0 / .7);
48
+ }
49
+
50
+ }
51
+
52
+ .dialogInner {
53
+ width: 100%;
54
+ height: 100%;
55
+ padding-inline-end: var(--size-md);
56
+ overflow-x: hidden;
57
+ overflow-y: auto;
58
+ /* scrollbar-gutter: stable; */
59
+ border-radius: var(--modal-border-radius);
60
+
61
+ /* stylelint-disable selector-class-pattern */
62
+ .padding-std & {
63
+ padding: var(--size-md) 0;
64
+ }
65
+ .padding-none & {
66
+ padding: 0;
67
+ }
68
+ /* stylelint-enable selector-class-pattern */
69
+ }
70
+
71
+ .modalSpinner {
72
+ position: relative;
73
+ padding-block-start: var(--size-2xl);
74
+
75
+ .dialog.on & {
76
+ display: none;
77
+ }
78
+ }
79
+
80
+ .iframeEl, .contentEl {
81
+ width: 100%;
82
+ overscroll-behavior: contain;
83
+ }
84
+
85
+ .iframeEl {
86
+ height: 100%;
87
+ padding: 0;
88
+ margin: 0;
89
+ border: none;
90
+ }
91
+
92
+ .contentEl {
93
+ min-height: 100%;
94
+ }
95
+
96
+ .closeButton {
97
+ --btn-color: var(--secondary-fg, #000);
98
+ --btn-bg-color: var(--secondary, #fff);
99
+ --btn-hover-color: var(--secondary, #fff);
100
+ --btn-hover-bg-color: var(--secondary-fg, #000);
101
+ --icon-size: 2.5rem;
102
+ --btn-padding: var(--size-xs, .5rem);
103
+ --icon-bars-thickness: 3px;
104
+
105
+ position: absolute;
106
+ top: calc(-1 * var(--icon-size) / 3);
107
+ right: calc(-1 * var(--icon-size) / 3);
108
+ display: block;
109
+ width: var(--icon-size);
110
+ height: var(--icon-size);
111
+ padding: var(--btn-padding);
112
+ color: var(--btn-color);
113
+ cursor: pointer;
114
+ background-color: var(--btn-bg-color);
115
+ /* border: var(--icon-bars-thickness) solid currentColor; */
116
+ border: none;
117
+ border-radius: 50%;
118
+
119
+
120
+ &:hover {
121
+ --btn-color: var(--btn-hover-color);
122
+ --btn-bg-color: var(--btn-hover-bg-color);
123
+ }
124
+
125
+ &::before, &::after {
126
+ position: absolute;
127
+ top: 50%;
128
+ left: var(--btn-padding);
129
+ display: block;
130
+ width: calc(var(--icon-size) - (var(--btn-padding) * 2));
131
+ height: var(--icon-bars-thickness);
132
+ content: '';
133
+ background: currentColor;
134
+ }
135
+ &::before {
136
+ transform: translateY(-50%) rotate(45deg);
137
+ }
138
+ &::after {
139
+ transform: translateY(-50%) rotate(-45deg);
140
+ }
141
+ }
142
+
@@ -119,6 +119,7 @@ input[type='checkbox'] {
119
119
 
120
120
  /* da bs5 */
121
121
  /* versione con label a destra */
122
+ /* TODO rivedere. sostituire float con flex? */
122
123
  .form-check {
123
124
  display: block;
124
125
  padding-left: 1.5em;
@@ -128,7 +129,7 @@ input[type='checkbox'] {
128
129
  float: left;
129
130
  display: inline-block;
130
131
  width: auto;
131
- margin: 4px 0 0 -1.5em;
132
+ margin: 4px 0 0 -1.5em;
132
133
  }
133
134
  }
134
135
 
@@ -0,0 +1,105 @@
1
+ /* TODO completare, aggiungere focus, vedere soluzione bootstrap
2
+
3
+
4
+ .form-switch {
5
+ padding-left: $form-switch-padding-start;
6
+
7
+ .form-check-input {
8
+ --#{$prefix}form-switch-bg: #{escape-svg($form-switch-bg-image)};
9
+
10
+ width: $form-switch-width;
11
+ margin-left: $form-switch-padding-start * -1;
12
+ background-image: var(--#{$prefix}form-switch-bg);
13
+ background-position: left center;
14
+ @include border-radius($form-switch-border-radius, 0);
15
+ @include transition($form-switch-transition);
16
+
17
+ &:focus {
18
+ --#{$prefix}form-switch-bg: #{escape-svg($form-switch-focus-bg-image)};
19
+ }
20
+
21
+ &:checked {
22
+ background-position: $form-switch-checked-bg-position;
23
+
24
+ @if $enable-gradients {
25
+ --#{$prefix}form-switch-bg: #{escape-svg($form-switch-checked-bg-image)}, var(--#{$prefix}gradient);
26
+ } @else {
27
+ --#{$prefix}form-switch-bg: #{escape-svg($form-switch-checked-bg-image)};
28
+ }
29
+ }
30
+ }
31
+
32
+ &.form-check-reverse {
33
+ padding-right: $form-switch-padding-start;
34
+ padding-left: 0;
35
+
36
+ .form-check-input {
37
+ margin-right: $form-switch-padding-start * -1;
38
+ margin-left: 0;
39
+ }
40
+ }
41
+ }
42
+
43
+
44
+ */
45
+
46
+ /*
47
+ <div class="switch">
48
+ <input type="checkbox">
49
+ <span class="switch-track"></span>
50
+ <label>Abilita notifiche</label>
51
+ </div>
52
+ */
53
+
54
+ .switch {
55
+ display: inline-flex;
56
+ align-items: center;
57
+ cursor: pointer;
58
+ }
59
+
60
+ .switch input {
61
+ position: absolute;
62
+ opacity: 0;
63
+ width: 0;
64
+ height: 0;
65
+ }
66
+
67
+ .switch-track {
68
+ position: relative;
69
+ width: 2.5em;
70
+ height: 1.5em;
71
+ background-color: var(--switch-background-color);
72
+ border-radius: 1.5em;
73
+ transition: background-color 0.2s;
74
+ }
75
+
76
+ .switch label {
77
+ margin-left: .5em;
78
+ user-select: none;
79
+ }
80
+
81
+ .switch-track::after {
82
+ content: '';
83
+ position: absolute;
84
+ top: 2px;
85
+ left: 2px;
86
+ width: 2em;
87
+ height: 2em;
88
+ background: var(--switch-cursor-background-color);
89
+ border-radius: 50%;
90
+ transition: translate 0.2s;
91
+ }
92
+
93
+ .switch input:checked + .switch-track {
94
+ background-color: var(--switch-checked-background-color);
95
+ }
96
+
97
+ .switch input:checked + .switch-track::after {
98
+ translate: 20px 0;
99
+ }
100
+
101
+ /* accessibilità */
102
+ .switch input:focus-visible + .switch-track {
103
+ outline: 2px solid var(--switch-checked-background-color);
104
+ outline-offset: 2px;
105
+ }
@@ -64,6 +64,9 @@
64
64
  .mie {
65
65
  margin-inline-end: var(--size-base) !important;
66
66
  }
67
+ .mie-auto {
68
+ margin-inline-end: auto !important;
69
+ }
67
70
  .mie-xs {
68
71
  margin-inline-end: var(--size-xs) !important;
69
72
  }
package/src/css/table.css CHANGED
@@ -58,6 +58,10 @@
58
58
  line-height: var(--table-cell-line-height);
59
59
  vertical-align: top;
60
60
  text-align: start;
61
+
62
+ .valign-middle &, &.valign-middle {
63
+ vertical-align: middle;
64
+ }
61
65
  }
62
66
 
63
67
  &.table-bordered {
@@ -273,6 +273,9 @@
273
273
  --status-warning-border-color: var(--status-warning-color);
274
274
  --status-warning-color: #fc0;
275
275
  --status-warning-fg: #4f2900;
276
+ --switch-background-color: var(--neutral-100);
277
+ --switch-checked-background-color: var(--primary-100);
278
+ --switch-cursor-background-color: var(--body-background-color);
276
279
  --table-body-border-width: .0625rem;
277
280
  --table-body-tr-alt-bg-color: #efefef;
278
281
  --table-body-tr-hover-bg-color: #cbcbcb;
@@ -36,6 +36,9 @@ export function parseDomString(domString) {
36
36
  // /(?: +(?<content>.*))?$/.source // content
37
37
  // );
38
38
 
39
+
40
+ // FIX non funziona se la stringa contine id e classi in quest'ordine: <TAG>.<CLASSE>#<ID>, se l'id è prima della classe, funziona
41
+
39
42
  const regex = new RegExp(
40
43
  /^([a-zA-Z][a-zA-Z0-9-]*)?/.source + // tag (accetta anche nomi di web components)
41
44
  /(?:#([a-zA-Z0-9_-]+))?/.source + // id
@@ -1,6 +1,6 @@
1
1
 
2
2
  // uso
3
- // throw new ValidationError('Sono presenti cicli duplicati', campiErrore );
3
+ // throw new ValidationError('Sono presenti valori duplicati', campiErrore );
4
4
 
5
5
  /*
6
6
  esempio:
@@ -579,14 +579,14 @@ class SimpleDatatableAdapter extends HTMLElement {
579
579
  // Per colonne con type 'number' o 'date' il valore viene lasciato invariato.
580
580
  const sortValue = col_item._sortValue
581
581
  ? (() => {
582
- const raw = typeof col_item._sortValue === 'function'
583
- ? (col_item._sortValue(row) ?? '')
584
- : (getNestedValue(row, col_item._sortValue) ?? '');
585
- const colType = col_item.type ?? 'string';
586
- return (colType === 'string' && typeof raw === 'number')
587
- ? String(raw)
588
- : raw;
589
- })()
582
+ const raw = typeof col_item._sortValue === 'function'
583
+ ? (col_item._sortValue(row) ?? '')
584
+ : (getNestedValue(row, col_item._sortValue) ?? '');
585
+ const colType = col_item.type ?? 'string';
586
+ return (colType === 'string' && typeof raw === 'number')
587
+ ? String(raw)
588
+ : raw;
589
+ })()
590
590
  : null;
591
591
 
592
592
 
@@ -51,11 +51,12 @@ export function parseCols(col_obj){
51
51
  };
52
52
 
53
53
  // campi booleani
54
- } else if (col_obj.type === 'boolean') {
54
+ } else if (col_obj.type === 'boolean' || col_obj._renderMode === 'bool_true_only') {
55
55
  // TODO caso in cui il valore è null nel caso si voglia differenziarlo da false
56
56
 
57
57
  return {
58
58
  ...col_obj,
59
+ type: 'boolean',
59
60
  cellClass: styles.hasIcon,
60
61
  headerClass: 'text-center',
61
62
  render: (value, td) => {
@@ -65,7 +66,7 @@ export function parseCols(col_obj){
65
66
  return checkIcon;
66
67
  } else {
67
68
  td.attributes.class = classnames(td.attributes.class, 'text-danger');
68
- return xIcon;
69
+ return col_obj._renderMode === 'bool_true_only'? '' : xIcon;
69
70
  }
70
71
  }
71
72
  };
@@ -121,13 +122,13 @@ export function parseCols(col_obj){
121
122
  return {
122
123
 
123
124
  cellClass: 'text-end text-numeric',
124
- headerClass: 'text-end',
125
+ headerClass: 'text-end text-numeric', // NB: usata anche per il footer
125
126
  };
126
127
 
127
128
  } else if(col_obj._renderMode === 'euro_no_dec') {
128
129
  return {
129
130
  render: (value, cell) => {
130
- console.log(value);
131
+ // console.log(value);
131
132
  if(value == null || isNaN(value)) {
132
133
  cell.attributes['data-order'] = '';
133
134
  return '\u2014';
@@ -142,7 +143,7 @@ export function parseCols(col_obj){
142
143
  }
143
144
  },
144
145
  cellClass: 'text-nowrap text-end text-numeric',
145
- headerClass: 'text-end',
146
+ headerClass: 'text-nowrap text-end text-numeric', // NB: usata anche per il footer
146
147
  type: 'number'
147
148
  };
148
149