kmaterialize 2.3.3-1.0.23 → 2.3.3-1.0.24
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/components/color-input/color-input.ts +2 -1
- package/components/kanban/_kanban.scss +8 -3
- package/components/kanban/kanban.ts +63 -3
- package/components/textfield/_input-fields.scss +7 -2
- package/dist/css/materialize.colors.min.css +1 -1
- package/dist/css/materialize.css +19 -6
- package/dist/css/materialize.min.css +2 -2
- package/dist/js/materialize.cjs.js +53 -6
- package/dist/js/materialize.d.ts +13 -1
- package/dist/js/materialize.js +53 -6
- package/dist/js/materialize.min.js +2 -2
- package/dist/js/materialize.mjs +53 -6
- package/package.json +1 -1
|
@@ -116,7 +116,8 @@ export class ColorInput extends Component<ColorInputOptions> {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
_handleInputChange = () => {
|
|
119
|
-
|
|
119
|
+
// A programmatic sync must not emit save and recursively dispatch change.
|
|
120
|
+
this.pickr?.setColor(this.el.value, true);
|
|
120
121
|
};
|
|
121
122
|
|
|
122
123
|
_handleLabelClick = (event: MouseEvent) => {
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
.kanban-board {
|
|
2
|
+
--kanban-zoom: 1;
|
|
2
3
|
--kanban-column-width: 20rem;
|
|
3
4
|
--kanban-gap: 1rem;
|
|
4
5
|
display: flex;
|
|
5
6
|
align-items: flex-start;
|
|
6
|
-
gap: var(--kanban-gap);
|
|
7
|
+
gap: calc(var(--kanban-gap) * var(--kanban-zoom));
|
|
7
8
|
width: 100%;
|
|
8
9
|
padding: 0.25rem;
|
|
9
10
|
overflow-x: auto;
|
|
10
11
|
box-sizing: border-box;
|
|
11
12
|
overscroll-behavior-x: contain;
|
|
12
13
|
|
|
14
|
+
// Unlike transform, zoom reserves the scaled space in the flex layout.
|
|
15
|
+
// This also keeps wrapping and the scrollable area aligned with the cards.
|
|
16
|
+
& > .kanban-column { zoom: var(--kanban-zoom); }
|
|
17
|
+
|
|
13
18
|
&.kanban-wrap {
|
|
14
19
|
flex-wrap: wrap;
|
|
15
20
|
overflow-x: visible;
|
|
@@ -79,8 +84,8 @@
|
|
|
79
84
|
padding: 1rem;
|
|
80
85
|
border: 1px solid var(--md-sys-color-outline-variant);
|
|
81
86
|
border-radius: 0.75rem;
|
|
82
|
-
background: var(--md-sys-color-surface);
|
|
83
|
-
color: var(--md-sys-color-on-surface);
|
|
87
|
+
background: var(--kanban-card-color, var(--md-sys-color-surface));
|
|
88
|
+
color: var(--kanban-card-text, var(--md-sys-color-on-surface));
|
|
84
89
|
box-shadow: var(--md-sys-elevation-level1, 0 1px 2px rgba(0, 0, 0, 0.14));
|
|
85
90
|
cursor: grab;
|
|
86
91
|
user-select: none;
|
|
@@ -11,6 +11,20 @@ export interface KanbanOptions extends BaseOptions {
|
|
|
11
11
|
draggable: boolean;
|
|
12
12
|
/** Called after a card changes column or order. */
|
|
13
13
|
onMove?: (detail: KanbanMoveDetail) => void;
|
|
14
|
+
zoom?: number;
|
|
15
|
+
minZoom?: number;
|
|
16
|
+
maxZoom?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Optional per-card appearance overrides. */
|
|
20
|
+
export interface KanbanCardAppearance {
|
|
21
|
+
/** Exact background color, overriding the Materialize surface color. */
|
|
22
|
+
color?: string;
|
|
23
|
+
/** Optional text and icon color. */
|
|
24
|
+
textColor?: string;
|
|
25
|
+
/** Optional edge accent color and position. */
|
|
26
|
+
accent?: string;
|
|
27
|
+
accentPosition?: 'top' | 'right' | 'bottom' | 'left';
|
|
14
28
|
}
|
|
15
29
|
|
|
16
30
|
const _defaults: KanbanOptions = {
|
|
@@ -19,6 +33,7 @@ const _defaults: KanbanOptions = {
|
|
|
19
33
|
|
|
20
34
|
/** A lightweight, dependency-free board for columns of draggable cards. */
|
|
21
35
|
export class Kanban extends Component<KanbanOptions> {
|
|
36
|
+
private _zoom = 1;
|
|
22
37
|
private _draggedCard: HTMLElement | null = null;
|
|
23
38
|
private _dragSourceColumn: HTMLElement | null = null;
|
|
24
39
|
private _dropPreview: HTMLElement | null = null;
|
|
@@ -30,13 +45,15 @@ export class Kanban extends Component<KanbanOptions> {
|
|
|
30
45
|
if (!card || !this.options.draggable || card.classList.contains('is-disabled') || card.getAttribute('aria-disabled') === 'true') return;
|
|
31
46
|
this._draggedCard = card;
|
|
32
47
|
this._dragSourceColumn = card.closest<HTMLElement>('.kanban-column');
|
|
33
|
-
this._previewHeight =
|
|
48
|
+
this._previewHeight = card.offsetHeight;
|
|
34
49
|
card.classList.add('is-dragging');
|
|
35
50
|
this._dragImage = card.cloneNode(true) as HTMLElement;
|
|
36
51
|
this._dragImage.classList.remove('is-dragging');
|
|
37
52
|
this._dragImage.classList.add('kanban-drag-image');
|
|
38
53
|
this._dragImage.setAttribute('aria-hidden', 'true');
|
|
39
|
-
this._dragImage.style.width = `${card.
|
|
54
|
+
this._dragImage.style.width = `${card.offsetWidth}px`;
|
|
55
|
+
this._dragImage.style.transform = `scale(${this._zoom})`;
|
|
56
|
+
this._dragImage.style.transformOrigin = 'top left';
|
|
40
57
|
document.body.appendChild(this._dragImage);
|
|
41
58
|
event.dataTransfer?.setDragImage(this._dragImage, card.offsetWidth / 2, card.offsetHeight / 2);
|
|
42
59
|
event.dataTransfer?.setData('text/plain', card.dataset.kanbanCard || '');
|
|
@@ -81,13 +98,41 @@ export class Kanban extends Component<KanbanOptions> {
|
|
|
81
98
|
constructor(el: HTMLElement, options: Partial<KanbanOptions>) {
|
|
82
99
|
super(el, options, Kanban);
|
|
83
100
|
this.options = { ...Kanban.defaults, ...options };
|
|
101
|
+
this._zoom = this._clampZoom(this.options.zoom ?? 1);
|
|
84
102
|
this.el['M_Kanban'] = this;
|
|
85
103
|
this._prepareMarkup();
|
|
104
|
+
this._applyZoom();
|
|
86
105
|
if (this.options.draggable) this._bindEvents();
|
|
87
106
|
}
|
|
88
107
|
|
|
89
108
|
static get defaults(): KanbanOptions {
|
|
90
|
-
return _defaults;
|
|
109
|
+
return { ..._defaults, zoom: 1, minZoom: 0.5, maxZoom: 2 };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Set the board scale, clamped to the configured limits. */
|
|
113
|
+
setZoom(value: number): void {
|
|
114
|
+
if (!Number.isFinite(value)) throw new TypeError('Zoom must be finite.');
|
|
115
|
+
this._zoom = this._clampZoom(value);
|
|
116
|
+
this._applyZoom();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Return the current board scale multiplier. */
|
|
120
|
+
getZoom(): number { return this._zoom; }
|
|
121
|
+
|
|
122
|
+
/** Restore the board to 100% scale. */
|
|
123
|
+
resetZoom(): void { this.setZoom(1); }
|
|
124
|
+
|
|
125
|
+
private _clampZoom(value: number): number {
|
|
126
|
+
if (!Number.isFinite(value)) throw new TypeError('Zoom must be finite.');
|
|
127
|
+
const min = this.options.minZoom ?? 0.5;
|
|
128
|
+
const max = this.options.maxZoom ?? 2;
|
|
129
|
+
if (!Number.isFinite(min) || !Number.isFinite(max) || min <= 0 || max < min) throw new TypeError('Invalid zoom limits.');
|
|
130
|
+
return Math.min(max, Math.max(min, value));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private _applyZoom(): void {
|
|
134
|
+
this.el.style.setProperty('--kanban-zoom', String(this._zoom));
|
|
135
|
+
this.el.dataset.zoom = String(this._zoom);
|
|
91
136
|
}
|
|
92
137
|
|
|
93
138
|
static init(el: HTMLElement, options?: Partial<KanbanOptions>): Kanban;
|
|
@@ -107,6 +152,21 @@ export class Kanban extends Component<KanbanOptions> {
|
|
|
107
152
|
const body = column.querySelector<HTMLElement>('.kanban-column-body');
|
|
108
153
|
if (body) body.setAttribute('role', 'list');
|
|
109
154
|
column.querySelectorAll<HTMLElement>('.kanban-card').forEach((card) => {
|
|
155
|
+
// Optional inline item colors override the Materialize defaults while
|
|
156
|
+
// keeping the public markup independent from the Sass implementation.
|
|
157
|
+
const color = card.dataset.kanbanColor;
|
|
158
|
+
const textColor = card.dataset.kanbanTextColor;
|
|
159
|
+
const accent = card.dataset.kanbanAccent;
|
|
160
|
+
const accentPosition = card.dataset.kanbanAccentPosition;
|
|
161
|
+
if (color && CSS.supports('color', color)) card.style.setProperty('--kanban-card-color', color);
|
|
162
|
+
if (textColor && CSS.supports('color', textColor)) card.style.setProperty('--kanban-card-text', textColor);
|
|
163
|
+
if (accent && CSS.supports('color', accent)) {
|
|
164
|
+
card.style.setProperty('--kanban-card-accent', accent);
|
|
165
|
+
card.classList.add('kanban-card-accent');
|
|
166
|
+
}
|
|
167
|
+
if (['top', 'right', 'bottom', 'left'].includes(accentPosition || '')) {
|
|
168
|
+
card.classList.add(`kanban-card-accent-${accentPosition}`);
|
|
169
|
+
}
|
|
110
170
|
card.setAttribute('role', 'listitem');
|
|
111
171
|
const disabled = card.classList.contains('is-disabled') || card.getAttribute('aria-disabled') === 'true';
|
|
112
172
|
card.setAttribute('tabindex', disabled ? '-1' : '0');
|
|
@@ -163,16 +163,21 @@ textarea.materialize-textarea {
|
|
|
163
163
|
user-select: none;
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
.prefix ~ input, .prefix ~ textarea {
|
|
166
|
+
.prefix ~ input:not([type=checkbox]):not([type=radio]), .prefix ~ textarea {
|
|
167
167
|
padding-left: calc(12px + 24px + 16px);
|
|
168
168
|
}
|
|
169
|
-
.suffix ~ input, .suffix ~ textarea {
|
|
169
|
+
.suffix ~ input:not([type=checkbox]):not([type=radio]), .suffix ~ textarea {
|
|
170
170
|
padding-right: calc(12px + 24px + 16px);
|
|
171
171
|
}
|
|
172
172
|
.prefix ~ label {
|
|
173
173
|
left: calc(12px + 24px + 16px);
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
// Reserve space beneath an already-floating label even before focus.
|
|
177
|
+
&:not(.outlined) > input:not([type=checkbox]):not([type=radio]):not(:focus):not(:placeholder-shown) {
|
|
178
|
+
padding-top: 20px;
|
|
179
|
+
}
|
|
180
|
+
|
|
176
181
|
// Outlined
|
|
177
182
|
|
|
178
183
|
// todo: use this approach for floating labels with outline
|
package/dist/css/materialize.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Materialize v2.3.3-1.0.
|
|
2
|
+
* Materialize v2.3.3-1.0.24 (https://materializeweb.com)
|
|
3
3
|
* Copyright 2014-2026 Materialize
|
|
4
4
|
* MIT License (https://raw.githubusercontent.com/materializecss/materialize/master/LICENSE)
|
|
5
5
|
*/
|
|
@@ -13864,6 +13864,7 @@ button.btn-floating {
|
|
|
13864
13864
|
}
|
|
13865
13865
|
}
|
|
13866
13866
|
.kanban-board {
|
|
13867
|
+
--kanban-zoom: 1;
|
|
13867
13868
|
--kanban-column-width: 20rem;
|
|
13868
13869
|
--kanban-gap: 1rem;
|
|
13869
13870
|
display: -webkit-box;
|
|
@@ -13874,7 +13875,7 @@ button.btn-floating {
|
|
|
13874
13875
|
-webkit-align-items: flex-start;
|
|
13875
13876
|
-ms-flex-align: start;
|
|
13876
13877
|
align-items: flex-start;
|
|
13877
|
-
gap: var(--kanban-gap);
|
|
13878
|
+
gap: calc(var(--kanban-gap) * var(--kanban-zoom));
|
|
13878
13879
|
width: 100%;
|
|
13879
13880
|
padding: 0.25rem;
|
|
13880
13881
|
overflow-x: auto;
|
|
@@ -13882,6 +13883,9 @@ button.btn-floating {
|
|
|
13882
13883
|
box-sizing: border-box;
|
|
13883
13884
|
overscroll-behavior-x: contain;
|
|
13884
13885
|
}
|
|
13886
|
+
.kanban-board > .kanban-column {
|
|
13887
|
+
zoom: var(--kanban-zoom);
|
|
13888
|
+
}
|
|
13885
13889
|
.kanban-board.kanban-wrap {
|
|
13886
13890
|
-webkit-flex-wrap: wrap;
|
|
13887
13891
|
-ms-flex-wrap: wrap;
|
|
@@ -13998,8 +14002,8 @@ button.btn-floating {
|
|
|
13998
14002
|
padding: 1rem;
|
|
13999
14003
|
border: 1px solid var(--md-sys-color-outline-variant);
|
|
14000
14004
|
border-radius: 0.75rem;
|
|
14001
|
-
background: var(--md-sys-color-surface);
|
|
14002
|
-
color: var(--md-sys-color-on-surface);
|
|
14005
|
+
background: var(--kanban-card-color, var(--md-sys-color-surface));
|
|
14006
|
+
color: var(--kanban-card-text, var(--md-sys-color-on-surface));
|
|
14003
14007
|
-webkit-box-shadow: var(--md-sys-elevation-level1, 0 1px 2px rgba(0, 0, 0, 0.14));
|
|
14004
14008
|
box-shadow: var(--md-sys-elevation-level1, 0 1px 2px rgba(0, 0, 0, 0.14));
|
|
14005
14009
|
cursor: -webkit-grab;
|
|
@@ -19009,15 +19013,24 @@ textarea.materialize-textarea {
|
|
|
19009
19013
|
-ms-user-select: none;
|
|
19010
19014
|
user-select: none;
|
|
19011
19015
|
}
|
|
19012
|
-
.input-field .prefix ~ input, .input-field .prefix ~ textarea {
|
|
19016
|
+
.input-field .prefix ~ input:not([type=checkbox]):not([type=radio]), .input-field .prefix ~ textarea {
|
|
19013
19017
|
padding-left: 52px;
|
|
19014
19018
|
}
|
|
19015
|
-
.input-field .suffix ~ input, .input-field .suffix ~ textarea {
|
|
19019
|
+
.input-field .suffix ~ input:not([type=checkbox]):not([type=radio]), .input-field .suffix ~ textarea {
|
|
19016
19020
|
padding-right: 52px;
|
|
19017
19021
|
}
|
|
19018
19022
|
.input-field .prefix ~ label {
|
|
19019
19023
|
left: 52px;
|
|
19020
19024
|
}
|
|
19025
|
+
.input-field:not(.outlined) > input:not([type=checkbox]):not([type=radio]):not(:focus):not(:-moz-placeholder) {
|
|
19026
|
+
padding-top: 20px;
|
|
19027
|
+
}
|
|
19028
|
+
.input-field:not(.outlined) > input:not([type=checkbox]):not([type=radio]):not(:focus):not(:-ms-input-placeholder) {
|
|
19029
|
+
padding-top: 20px;
|
|
19030
|
+
}
|
|
19031
|
+
.input-field:not(.outlined) > input:not([type=checkbox]):not([type=radio]):not(:focus):not(:placeholder-shown) {
|
|
19032
|
+
padding-top: 20px;
|
|
19033
|
+
}
|
|
19021
19034
|
.input-field.outlined input:not([type=checkbox]):not([type=radio]), .input-field.outlined textarea {
|
|
19022
19035
|
padding-top: 0;
|
|
19023
19036
|
background-color: transparent;
|