kmaterialize 2.3.3-1.0.2 → 2.3.3-1.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +4 -4
  2. package/components/air-datepicker/_air-datepicker.scss +137 -0
  3. package/components/air-datepicker/air-datepicker.ts +192 -0
  4. package/components/alert/_alerts.scss +130 -0
  5. package/components/alert/alert.ts +73 -0
  6. package/components/appbar/appbar.css +301 -4
  7. package/components/button/_buttons.scss +39 -1
  8. package/components/card/_cards.scss +9 -1
  9. package/components/checkbox/_checkboxes.scss +120 -229
  10. package/components/collapsible/_collapsible.scss +50 -0
  11. package/components/color-input/_color-input.scss +124 -0
  12. package/components/color-input/color-input.ts +203 -0
  13. package/components/dropdown/dropdown.ts +1 -1
  14. package/components/file-input/_file-input-enhanced.scss +61 -0
  15. package/components/file-input/file-input.ts +203 -0
  16. package/components/kanban/_kanban.scss +148 -0
  17. package/components/kanban/kanban.ts +133 -0
  18. package/components/list/_collection.scss +10 -2
  19. package/components/number-input/number-input.ts +103 -0
  20. package/components/password-input/password-input.ts +72 -0
  21. package/components/slider/_range.scss +5 -1
  22. package/components/switch/_switches.scss +5 -0
  23. package/components/textfield/_input-fields.scss +4 -4
  24. package/components/textfield/_select.scss +54 -0
  25. package/components/tom-select/_tom-select-field.scss +256 -0
  26. package/components/tom-select/tom-select-field.ts +175 -0
  27. package/components/toolbar/_toolbar.scss +324 -0
  28. package/components/toolbar/toolbar.ts +152 -0
  29. package/dist/css/materialize.colors.min.css +2 -2
  30. package/dist/css/materialize.css +10575 -711
  31. package/dist/css/materialize.min.css +2 -2
  32. package/dist/css/materialize.min.css.map +1 -1
  33. package/dist/js/materialize.cjs.js +1136 -45
  34. package/dist/js/materialize.d.ts +227 -2
  35. package/dist/js/materialize.js +1136 -45
  36. package/dist/js/materialize.min.js +2 -2
  37. package/dist/js/materialize.mjs +1128 -46
  38. package/package.json +45 -1
  39. package/sass/_colors.scss +191 -5
  40. package/sass/_global.scss +240 -1
  41. package/sass/materialize.scss +14 -1
  42. package/sass/mixins.module.scss +6 -6
@@ -0,0 +1,256 @@
1
+ // Materialize styling for the wrapper generated after select.tomselected.
2
+ .input-field:has(> select.tomselected) {
3
+ margin-top: 16px;
4
+ }
5
+
6
+ .input-field > .ts-wrapper {
7
+ margin-top: 5px;
8
+
9
+ &.single > .ts-control,
10
+ &.multi > .ts-control {
11
+ box-sizing: border-box;
12
+ display: flex;
13
+ align-items: center;
14
+ width: 100%;
15
+ height: 56px;
16
+ min-height: 56px;
17
+ padding: 0 0 0 15px !important;
18
+ overflow: hidden;
19
+ background: var(--md-sys-color-background) !important;
20
+ background-image: none !important;
21
+ border: 0 !important;
22
+ border-bottom: 1px solid var(--md-sys-color-on-surface-variant) !important;
23
+ border-radius: 4px 4px 0 0;
24
+ box-shadow: none !important;
25
+ }
26
+
27
+ &.focus > .ts-control {
28
+ border-bottom: 2px solid var(--input-color) !important;
29
+ }
30
+
31
+ // Join the control visually to its open dropdown. Closed controls retain
32
+ // their normal corner radius.
33
+ &.dropdown-active > .ts-control {
34
+ border-bottom-right-radius: 0 !important;
35
+ border-bottom-left-radius: 0 !important;
36
+ }
37
+
38
+ .ts-control {
39
+ font-size: 16px;
40
+ color: var(--md-sys-color-on-surface);
41
+
42
+ > input,
43
+ input[role="combobox"] {
44
+ box-sizing: border-box;
45
+ width: auto !important;
46
+ height: auto !important;
47
+ min-height: 0 !important;
48
+ margin: 0 !important;
49
+ padding: 0 !important;
50
+ color: var(--md-sys-color-on-surface) !important;
51
+ font-size: 16px;
52
+ line-height: 24px;
53
+ background: transparent !important;
54
+ border: 0 !important;
55
+ border-radius: 0 !important;
56
+ box-shadow: none !important;
57
+
58
+ &::placeholder {
59
+ color: color-mix(in srgb, var(--md-sys-color-on-surface) 55%, transparent);
60
+ opacity: 1;
61
+ }
62
+ }
63
+ }
64
+
65
+ // Only multi-select values are rendered as chips. A single value remains
66
+ // normal input text, matching Materialize's select and text fields.
67
+ &.multi > .ts-control {
68
+ gap: 6px;
69
+ padding: 0 0 0 15px !important;
70
+ flex-wrap: nowrap;
71
+
72
+ .item {
73
+ flex: 0 0 auto;
74
+ margin: 0 !important;
75
+ padding: 4px 10px !important;
76
+ color: var(--md-sys-color-on-primary-container);
77
+ line-height: 20px;
78
+ background: var(--md-sys-color-primary-container);
79
+ border: 1px solid var(--md-sys-color-outline-variant);
80
+ border-radius: 999px;
81
+ }
82
+ }
83
+
84
+ &.single > .ts-control .item {
85
+ margin: 0 !important;
86
+ padding: 0 !important;
87
+ color: var(--md-sys-color-on-surface);
88
+ line-height: 24px;
89
+ background: transparent;
90
+ border: 0;
91
+ }
92
+
93
+ &.disabled > .ts-control {
94
+ opacity: 1;
95
+ color: color-mix(in srgb, transparent, var(--md-sys-color-on-surface) 38%);
96
+ background: color-mix(in srgb, transparent, var(--md-sys-color-on-surface) 4%) !important;
97
+ border-color: color-mix(in srgb, transparent, var(--md-sys-color-on-surface) 12%) !important;
98
+ }
99
+
100
+ // Tom Select hides the native caret, so draw one centered in the field.
101
+ &.single > .ts-control::after {
102
+ content: "";
103
+ position: absolute;
104
+ top: 50%;
105
+ right: 24px;
106
+ width: 9px;
107
+ height: 9px;
108
+ margin-top: -6px;
109
+ border: solid var(--md-sys-color-on-surface) !important;
110
+ border-width: 0 3px 3px 0 !important;
111
+ transform: rotate(45deg);
112
+ }
113
+
114
+ &.single.dropdown-active > .ts-control::after {
115
+ margin-top: -3px;
116
+ transform: rotate(-135deg);
117
+ }
118
+
119
+ .ts-dropdown {
120
+ z-index: 9999;
121
+ margin-top: 0;
122
+ color: var(--md-sys-color-on-surface);
123
+ background: var(--md-sys-color-background) !important;
124
+ border: 1px solid var(--md-sys-color-outline-variant);
125
+ border-radius: 0 0 4px 4px;
126
+ overflow: hidden;
127
+ box-shadow: 0 2px 2px #00000024, 0 3px 1px -2px #0000001f, 0 1px 5px #0003;
128
+
129
+ .ts-dropdown-content {
130
+ background: inherit;
131
+ }
132
+
133
+ .create,
134
+ .no-results,
135
+ .option {
136
+ display: block;
137
+ padding: 14px 16px;
138
+ color: var(--md-sys-color-on-surface);
139
+ font-size: 16px;
140
+ line-height: 22px;
141
+ background: transparent;
142
+
143
+ &.active {
144
+ background: var(--md-sys-color-primary-container);
145
+ }
146
+ }
147
+ }
148
+
149
+ .ts-option-content {
150
+ display: flex;
151
+ align-items: center;
152
+ gap: 10px;
153
+ width: 100%;
154
+
155
+ img {
156
+ width: 32px;
157
+ height: 32px;
158
+ object-fit: cover;
159
+ border-radius: 50%;
160
+ }
161
+ }
162
+ }
163
+
164
+ // Large multi-value sets can either grow onto additional rows or retain the
165
+ // standard field height and expose a horizontal scrollbar.
166
+ .input-field > select.tomselected[data-select-overflow="wrap"] + .ts-wrapper.multi > .ts-control {
167
+ height: auto;
168
+ min-height: 56px;
169
+ padding: 8px 0 8px 15px !important;
170
+ overflow: visible;
171
+ flex-wrap: wrap;
172
+ }
173
+
174
+ .input-field > select.tomselected[data-select-overflow="scroll"] + .ts-wrapper.multi > .ts-control {
175
+ overflow-x: auto;
176
+ overflow-y: hidden;
177
+ flex-wrap: nowrap;
178
+ scrollbar-color: var(--md-sys-color-outline) transparent;
179
+ scrollbar-width: thin;
180
+
181
+ &::-webkit-scrollbar {
182
+ height: 5px;
183
+ }
184
+
185
+ &::-webkit-scrollbar-thumb {
186
+ background: var(--md-sys-color-outline);
187
+ border-radius: 999px;
188
+ }
189
+ }
190
+
191
+ // Tom Select labels have the same two states as Materialize text fields.
192
+ // State 1: no value and no placeholder — label acts as the in-field prompt.
193
+ .input-field > select.tomselected + .ts-wrapper + label {
194
+ position: absolute !important;
195
+ top: 16px !important;
196
+ left: 16px !important;
197
+ z-index: 2;
198
+ margin: 0 !important;
199
+ padding: 0 !important;
200
+ color: var(--md-sys-color-on-surface-variant);
201
+ font-size: 16px !important;
202
+ line-height: 24px !important;
203
+ text-align: left !important;
204
+ background: transparent;
205
+ transform: none !important;
206
+ transform-origin: top left !important;
207
+ transition: top 0.2s ease-out, transform 0.2s ease-out, color 0.2s ease-out;
208
+ }
209
+
210
+ // State 2: a placeholder, a selected value, or focus — float from the same
211
+ // 16px base font/position used by Materialize text-input labels.
212
+ .input-field > select.tomselected[placeholder]:not([placeholder=""]) + .ts-wrapper + label,
213
+ .input-field > select.tomselected:has(> option[value=""]:checked:not(:empty)) + .ts-wrapper + label,
214
+ .input-field > select.tomselected + .ts-wrapper.has-items + label,
215
+ .input-field > select.tomselected + .ts-wrapper.focus + label {
216
+ position: absolute !important;
217
+ top: -12px !important;
218
+ left: 16px !important;
219
+ font-size: 16px !important;
220
+ transform: scale(0.75) !important;
221
+ }
222
+
223
+ .input-field > select.tomselected + .ts-wrapper.focus + label {
224
+ color: var(--input-color);
225
+ }
226
+
227
+ // Outlined fields use the same two states; only the floating position changes
228
+ // so the small label cuts a notch through the outline like a text input.
229
+ .input-field.outlined > .ts-wrapper.single > .ts-control,
230
+ .input-field.outlined > .ts-wrapper.multi > .ts-control {
231
+ padding: 0 0 0 15px !important;
232
+ background: transparent !important;
233
+ border: 1px solid var(--md-sys-color-on-surface-variant) !important;
234
+ border-radius: 4px;
235
+ }
236
+
237
+ .input-field.outlined > .ts-wrapper.focus > .ts-control {
238
+ border: 2px solid var(--input-color) !important;
239
+ }
240
+
241
+ .input-field.outlined > select.tomselected[placeholder]:not([placeholder=""]) + .ts-wrapper + label,
242
+ .input-field.outlined > select.tomselected:has(> option[value=""]:checked:not(:empty)) + .ts-wrapper + label,
243
+ .input-field.outlined > select.tomselected + .ts-wrapper.has-items + label,
244
+ .input-field.outlined > select.tomselected + .ts-wrapper.focus + label {
245
+ top: -8px !important;
246
+ left: 12px !important;
247
+ padding: 0 4px !important;
248
+ line-height: 16px !important;
249
+ background: var(--md-sys-color-surface);
250
+ transform: scale(0.75) !important;
251
+ }
252
+
253
+ div.suffix.suffix-tomselect {
254
+ z-index: 2;
255
+ top: 30px;
256
+ }
@@ -0,0 +1,175 @@
1
+ import type TomSelectCtor from 'tom-select';
2
+ import type { TomSettings } from 'tom-select/dist/esm/types/settings.js';
3
+ import type { RecursivePartial } from 'tom-select/dist/esm/types/core.js';
4
+ import { Component, BaseOptions, InitElements, MElement } from '../../src/component';
5
+ import { loadPeer } from '../../src/peer-loader';
6
+ import { FormSelect } from '../textfield/select';
7
+
8
+ export interface RemoteDataSpec {
9
+ url: string;
10
+ value: string;
11
+ label: string;
12
+ search?: string;
13
+ dataKey?: string;
14
+ }
15
+
16
+ export interface TomSelectFieldOptions extends BaseOptions {
17
+ remote?: RemoteDataSpec;
18
+ settings?: RecursivePartial<TomSettings>;
19
+ }
20
+
21
+ const _defaults: TomSelectFieldOptions = {};
22
+
23
+ function readRemoteSpec(el: HTMLSelectElement): RemoteDataSpec | undefined {
24
+ const raw = el.dataset.selectRemote;
25
+ if (!raw) return undefined;
26
+ try {
27
+ return JSON.parse(raw);
28
+ } catch {
29
+ return undefined;
30
+ }
31
+ }
32
+
33
+ // @implement /Users/kzarshenas/Sites/CrazyProject/CrazyPHP/src/Front/Library/Utility/Form/Select.ts
34
+ // Enhances an EXISTING native `<select class="tomselected">` with Tom
35
+ // Select - searchable, taggable, remote-loading, dependent-field wiring,
36
+ // clear button, drag-drop reorder. Ported in a somewhat scoped form: the
37
+ // CrazyPHP source's remote-loading path goes through its own app-specific
38
+ // Crazyrequest/Crazyurl HTTP-client and query-param helpers, which have no
39
+ // equivalent in a standalone UI library - this version uses a plain
40
+ // `fetch()` instead, keeping the same `data-select-remote` JSON shape
41
+ // (url/value/label/search/dataKey) and the same `{{field}}` render-template
42
+ // convention.
43
+ //
44
+ // Coexistence with FormSelect: `.tomselected` is a recognized exclusion
45
+ // marker in FormSelect's own AutoInit selector (see src/index.ts), so a
46
+ // `.tomselected` select is never picked up by FormSelect regardless of
47
+ // init ordering. This constructor still defensively tears down any
48
+ // FormSelect instance found on the same element, belt-and-suspenders, in
49
+ // case that selector rule is ever violated by hand.
50
+ //
51
+ // Tom Select is an optional peerDependency, loaded on demand via
52
+ // peer-loader.
53
+ export class TomSelectField extends Component<TomSelectFieldOptions> {
54
+ declare el: HTMLSelectElement;
55
+ tomSelect: TomSelectCtor | undefined;
56
+ ready: Promise<void>;
57
+ private _dependsOnEl: HTMLElement | null = null;
58
+
59
+ constructor(el: HTMLSelectElement, options: Partial<TomSelectFieldOptions>) {
60
+ super(el, options, TomSelectField);
61
+ (this.el as any).M_TomSelectField = this;
62
+
63
+ FormSelect.getInstance(this.el)?.destroy();
64
+
65
+ this.options = {
66
+ remote: readRemoteSpec(this.el),
67
+ ...TomSelectField.defaults,
68
+ ...options
69
+ };
70
+
71
+ this.ready = this._setup();
72
+ }
73
+
74
+ static get defaults(): TomSelectFieldOptions {
75
+ return _defaults;
76
+ }
77
+
78
+ static init(el: HTMLSelectElement, options?: Partial<TomSelectFieldOptions>): TomSelectField;
79
+ static init(
80
+ els: InitElements<HTMLSelectElement | MElement>,
81
+ options?: Partial<TomSelectFieldOptions>
82
+ ): TomSelectField[];
83
+ static init(
84
+ els: HTMLSelectElement | InitElements<HTMLSelectElement | MElement>,
85
+ options: Partial<TomSelectFieldOptions> = {}
86
+ ): TomSelectField | TomSelectField[] {
87
+ return super.init(els, options, TomSelectField);
88
+ }
89
+
90
+ static getInstance(el: HTMLSelectElement): TomSelectField {
91
+ return (el as any).M_TomSelectField;
92
+ }
93
+
94
+ destroy() {
95
+ if (this._dependsOnEl) this._dependsOnEl.removeEventListener('change', this._handleDependencyChange);
96
+ this.tomSelect?.destroy();
97
+ (this.el as any).M_TomSelectField = undefined;
98
+ }
99
+
100
+ _handleDependencyChange = () => {
101
+ this.tomSelect?.clear();
102
+ this.tomSelect?.clearOptions();
103
+ this.tomSelect?.load?.('');
104
+ };
105
+
106
+ async _setup(): Promise<void> {
107
+ const TomSelect = await loadPeer<typeof TomSelectCtor>(
108
+ {
109
+ specifier: 'tom-select',
110
+ globalName: 'TomSelect',
111
+ feature: 'Select (Tom Select) enhancement',
112
+ cdnHint: '<script src="path/to/tom-select.complete.min.js"></script> (self-hosted - copy from node_modules/tom-select/dist/js/, or a CDN of your choice)'
113
+ },
114
+ () => import('tom-select')
115
+ );
116
+
117
+ const dataset = this.el.dataset;
118
+ const settings: RecursivePartial<TomSettings> = {
119
+ persist: false,
120
+ createOnBlur: true,
121
+ create: 'selectTag' in dataset,
122
+ plugins: {},
123
+ ...this.options.settings
124
+ };
125
+
126
+ if ('selectClear' in dataset) {
127
+ (settings.plugins as any).clear_button = { title: dataset.selectClear || 'Clear' };
128
+ }
129
+ if ('selectTag' in dataset || this.el.multiple) {
130
+ (settings.plugins as any).caret_position = {};
131
+ (settings.plugins as any).drag_drop = {};
132
+ }
133
+
134
+ if (this.el.classList.contains('icons')) {
135
+ const renderWithIcon = (data: Record<string, any>, escape: (value: string) => string) => {
136
+ const icon = data.icon ? `<img src="${escape(String(data.icon))}" alt="" />` : '';
137
+ return `<div class="ts-option-content">${icon}<span>${escape(String(data.text ?? ''))}</span></div>`;
138
+ };
139
+ settings.render = { ...settings.render, option: renderWithIcon, item: renderWithIcon };
140
+ }
141
+
142
+ const remote = this.options.remote;
143
+ if (remote) {
144
+ settings.valueField = remote.value;
145
+ if (remote.search) settings.searchField = [remote.search];
146
+
147
+ if (remote.label && remote.label.includes('{{') && remote.label.includes('}}')) {
148
+ const renderTemplate = (data: Record<string, any>, escape: (input: string) => string) =>
149
+ `<div>${remote.label.replace(/\{\{(.*?)\}\}/g, (_m, key) => escape(String(data[key] ?? '')))}</div>`;
150
+ settings.render = { option: renderTemplate, item: renderTemplate };
151
+ } else {
152
+ settings.labelField = remote.label;
153
+ }
154
+
155
+ settings.load = (query, callback) => {
156
+ fetch(remote.url)
157
+ .then((r) => r.json())
158
+ .then((value) => {
159
+ const results = remote.dataKey
160
+ ? remote.dataKey.split('.').reduce((acc: any, key: string) => acc && acc[key], value)
161
+ : value?.results ?? value;
162
+ callback(Array.isArray(results) ? results : []);
163
+ })
164
+ .catch(() => callback([]));
165
+ };
166
+ }
167
+
168
+ this.tomSelect = new TomSelect(this.el, settings);
169
+
170
+ if (dataset.depends) {
171
+ this._dependsOnEl = document.querySelector<HTMLElement>(dataset.depends);
172
+ this._dependsOnEl?.addEventListener('change', this._handleDependencyChange);
173
+ }
174
+ }
175
+ }