mtrl 0.2.8 → 0.2.9

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/index.ts +2 -0
  2. package/package.json +1 -1
  3. package/src/components/navigation/api.ts +131 -96
  4. package/src/components/navigation/features/controller.ts +273 -0
  5. package/src/components/navigation/features/items.ts +133 -64
  6. package/src/components/navigation/navigation.ts +17 -2
  7. package/src/components/navigation/system-types.ts +124 -0
  8. package/src/components/navigation/system.ts +776 -0
  9. package/src/components/slider/config.ts +20 -2
  10. package/src/components/slider/features/controller.ts +761 -0
  11. package/src/components/slider/features/handlers.ts +18 -15
  12. package/src/components/slider/features/index.ts +3 -2
  13. package/src/components/slider/features/range.ts +104 -0
  14. package/src/components/slider/slider.ts +34 -14
  15. package/src/components/slider/structure.ts +152 -0
  16. package/src/components/textfield/api.ts +53 -0
  17. package/src/components/textfield/features.ts +322 -0
  18. package/src/components/textfield/textfield.ts +8 -0
  19. package/src/components/textfield/types.ts +12 -3
  20. package/src/components/timepicker/clockdial.ts +1 -4
  21. package/src/core/compose/features/textinput.ts +15 -2
  22. package/src/core/composition/features/dom.ts +33 -0
  23. package/src/core/composition/features/icon.ts +131 -0
  24. package/src/core/composition/features/index.ts +11 -0
  25. package/src/core/composition/features/label.ts +156 -0
  26. package/src/core/composition/features/structure.ts +22 -0
  27. package/src/core/composition/index.ts +26 -0
  28. package/src/core/index.ts +1 -1
  29. package/src/core/structure.ts +288 -0
  30. package/src/index.ts +1 -0
  31. package/src/styles/components/_navigation-mobile.scss +244 -0
  32. package/src/styles/components/_navigation-system.scss +151 -0
  33. package/src/styles/components/_textfield.scss +250 -11
  34. package/demo/build.ts +0 -349
  35. package/demo/index.html +0 -110
  36. package/demo/main.js +0 -448
  37. package/demo/styles.css +0 -239
  38. package/server.ts +0 -86
  39. package/src/components/slider/features/slider.ts +0 -318
  40. package/src/components/slider/features/structure.ts +0 -181
  41. package/src/components/slider/features/ui.ts +0 -388
  42. package/src/components/textfield/constants.ts +0 -100
package/demo/styles.css DELETED
@@ -1,239 +0,0 @@
1
- // demo/styles.scss
2
-
3
- // Import the main styles from the source code
4
- @use '../src/styles/main.scss';
5
-
6
- /* Base styles */
7
- :root {
8
- --primary-color: #6750a4;
9
- --on-primary-color: #ffffff;
10
- --primary-container-color: #eaddff;
11
- --on-primary-container-color: #21005d;
12
- --secondary-color: #625b71;
13
- --on-secondary-color: #ffffff;
14
- --secondary-container-color: #e8def8;
15
- --on-secondary-container-color: #1d192b;
16
- --tertiary-color: #7d5260;
17
- --surface-color: #fffbfe;
18
- --on-surface-color: #1c1b1f;
19
- --surface-variant-color: #e7e0ec;
20
- --on-surface-variant-color: #49454f;
21
- --outline-color: #79747e;
22
- --background-color: #fffbfe;
23
- --error-color: #b3261e;
24
-
25
- --elevation-1: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);
26
- --elevation-2: 0px 2px 6px 2px rgba(0, 0, 0, 0.15);
27
- --elevation-3: 0px 4px 8px 3px rgba(0, 0, 0, 0.15);
28
-
29
- --spacing-1: 4px;
30
- --spacing-2: 8px;
31
- --spacing-3: 12px;
32
- --spacing-4: 16px;
33
- --spacing-5: 24px;
34
- --spacing-6: 32px;
35
-
36
- --border-radius-small: 4px;
37
- --border-radius-medium: 8px;
38
- --border-radius-large: 16px;
39
- --border-radius-full: 9999px;
40
- }
41
-
42
- /* Dark theme support */
43
- @media (prefers-color-scheme: dark) {
44
- :root {
45
- --primary-color: #d0bcff;
46
- --on-primary-color: #381e72;
47
- --primary-container-color: #4f378b;
48
- --on-primary-container-color: #eaddff;
49
- --secondary-color: #ccc2dc;
50
- --on-secondary-color: #332d41;
51
- --secondary-container-color: #4a4458;
52
- --on-secondary-container-color: #e8def8;
53
- --tertiary-color: #efb8c8;
54
- --surface-color: #1c1b1f;
55
- --on-surface-color: #e6e1e5;
56
- --surface-variant-color: #49454f;
57
- --on-surface-variant-color: #cac4d0;
58
- --outline-color: #938f99;
59
- --background-color: #1c1b1f;
60
- --error-color: #f2b8b5;
61
- }
62
- }
63
-
64
- /* Core styles */
65
- * {
66
- box-sizing: border-box;
67
- margin: 0;
68
- padding: 0;
69
- }
70
-
71
- body {
72
- font-family: 'Roboto', sans-serif;
73
- background-color: var(--background-color);
74
- color: var(--on-surface-color);
75
- line-height: 1.5;
76
- }
77
-
78
- /* Layout */
79
- .content {
80
- max-width: 1200px;
81
- margin: 0 auto;
82
- padding: var(--spacing-5);
83
- margin-bottom: 120px; /* For bottom app bar */
84
- }
85
-
86
- /* Component Sections */
87
- .component-section {
88
- margin-bottom: var(--spacing-6);
89
- padding: var(--spacing-4);
90
- border-radius: var(--border-radius-medium);
91
- background-color: var(--surface-color);
92
- box-shadow: var(--elevation-1);
93
- }
94
-
95
- .component-section h2 {
96
- color: var(--primary-color);
97
- margin-bottom: var(--spacing-4);
98
- font-size: 1.5rem;
99
- border-bottom: 1px solid var(--outline-color);
100
- padding-bottom: var(--spacing-2);
101
- }
102
-
103
- .component-row {
104
- display: flex;
105
- flex-wrap: wrap;
106
- gap: var(--spacing-5);
107
- align-items: flex-start;
108
- }
109
-
110
- .align-top {
111
- align-items: flex-start;
112
- }
113
-
114
- /* Demo Blocks */
115
- .demo-block {
116
- flex: 1;
117
- min-width: 300px;
118
- margin-bottom: var(--spacing-4);
119
- }
120
-
121
- .demo-block h3 {
122
- font-size: 1rem;
123
- color: var(--on-surface-variant-color);
124
- margin-bottom: var(--spacing-3);
125
- }
126
-
127
- .demo-content {
128
- display: flex;
129
- flex-direction: column;
130
- gap: var(--spacing-3);
131
- }
132
-
133
- /* Intro section */
134
- #intro {
135
- text-align: center;
136
- margin-bottom: var(--spacing-6);
137
- padding: var(--spacing-6) 0;
138
- }
139
-
140
- #intro h1 {
141
- color: var(--primary-color);
142
- font-size: 2.5rem;
143
- margin-bottom: var(--spacing-3);
144
- }
145
-
146
- #intro p {
147
- color: var(--on-surface-variant-color);
148
- font-size: 1.2rem;
149
- max-width: 600px;
150
- margin: 0 auto;
151
- }
152
-
153
- /* App Bars */
154
- .top-app-bar {
155
- position: sticky;
156
- top: 0;
157
- z-index: 1000;
158
- }
159
-
160
- /* Carousel */
161
- .carousel-item {
162
- height: 200px;
163
- display: flex;
164
- align-items: center;
165
- justify-content: center;
166
- background-color: var(--primary-container-color);
167
- color: var(--on-primary-container-color);
168
- font-weight: bold;
169
- border-radius: var(--border-radius-medium);
170
- }
171
-
172
- /* Card Demo */
173
- .demo-card-media {
174
- height: 140px;
175
- background-color: var(--primary-container-color);
176
- background-image: linear-gradient(45deg, var(--primary-color) 25%, transparent 25%, transparent 50%, var(--primary-color) 50%, var(--primary-color) 75%, transparent 75%, transparent);
177
- background-size: 20px 20px;
178
- border-radius: var(--border-radius-medium) var(--border-radius-medium) 0 0;
179
- }
180
-
181
- /* Hack to ensure components like dialog, sheet, tooltip are visible in the demo */
182
- .mtrl-dialog, .mtrl-sheet, .mtrl-tooltip {
183
- position: absolute; /* Override fixed positioning for demo */
184
- z-index: 2; /* Lower z-index for demo */
185
- }
186
-
187
- /* Display helpers */
188
- .demo-text {
189
- margin: var(--spacing-2) 0;
190
- color: var(--on-surface-variant-color);
191
- }
192
-
193
- /* Responsive adjustments */
194
- @media (max-width: 768px) {
195
- .component-row {
196
- flex-direction: column;
197
- }
198
-
199
- .demo-block {
200
- width: 100%;
201
- }
202
- }
203
-
204
- /* Component-specific styling overrides for demo purposes */
205
- .mtrl-button {
206
- margin-right: var(--spacing-2);
207
- }
208
-
209
- .mtrl-textfield {
210
- width: 100%;
211
- max-width: 300px;
212
- }
213
-
214
- .mtrl-card {
215
- max-width: 320px;
216
- }
217
-
218
- .mtrl-list {
219
- max-width: 400px;
220
- border: 1px solid var(--outline-color);
221
- border-radius: var(--border-radius-medium);
222
- }
223
-
224
- .mtrl-slider {
225
- width: 100%;
226
- max-width: 300px;
227
- }
228
-
229
- .mtrl-navigation {
230
- width: 300px;
231
- border: 1px solid var(--outline-color);
232
- border-radius: var(--border-radius-medium);
233
- }
234
-
235
- .mtrl-tabs {
236
- width: 100%;
237
- max-width: 400px;
238
- border-bottom: 1px solid var(--outline-color);
239
- }
package/server.ts DELETED
@@ -1,86 +0,0 @@
1
- // server.ts - TypeScript version of the Bun server for the component library demo
2
- import { serve, type ServeOptions } from "bun";
3
- import { join } from "path";
4
- import { readFileSync, existsSync } from "fs";
5
-
6
- // Content type mapping
7
- const contentTypes: Record<string, string> = {
8
- ".html": "text/html",
9
- ".js": "text/javascript",
10
- ".mjs": "text/javascript", // Add this for ES modules
11
- ".css": "text/css",
12
- ".json": "application/json",
13
- ".png": "image/png",
14
- ".jpg": "image/jpeg",
15
- ".svg": "image/svg+xml"
16
- };
17
-
18
- // Define the port (hardcoded instead of using environment variable)
19
- const PORT: number = 3301;
20
-
21
- // Define demo directory path
22
- const DEMO_DIR: string = "./demo";
23
-
24
- /**
25
- * Serves a file from the demo directory
26
- * @param path Path to the file relative to the demo directory
27
- * @returns Response object with the file content or error message
28
- */
29
- function serveFile(path: string): Response {
30
- // Add demo directory to path
31
- const filePath: string = join(DEMO_DIR, path);
32
-
33
- // Handle index.html for directory request
34
- let resolvedPath: string = filePath;
35
- if (filePath.endsWith('/') || !filePath.includes('.')) {
36
- resolvedPath = join(filePath, 'index.html');
37
- }
38
-
39
- // Check if file exists
40
- if (!existsSync(resolvedPath)) {
41
- return new Response("File not found", { status: 404 });
42
- }
43
-
44
- // Get file extension for content type
45
- const ext: string = resolvedPath.substring(resolvedPath.lastIndexOf('.'));
46
- const contentType: string = contentTypes[ext] || "application/octet-stream";
47
-
48
- // Read and serve the file
49
- try {
50
- const content: Buffer = readFileSync(resolvedPath);
51
-
52
- // For JavaScript files, ensure they're served with the correct MIME type
53
- // and additionally add proper headers for modules
54
- if (ext === '.js') {
55
- return new Response(content, {
56
- headers: {
57
- "Content-Type": "text/javascript",
58
- "Cache-Control": "no-cache"
59
- }
60
- });
61
- }
62
-
63
- return new Response(content, {
64
- headers: { "Content-Type": contentType }
65
- });
66
- } catch (error) {
67
- console.error(`Error serving ${resolvedPath}:`, error);
68
- return new Response("Server error", { status: 500 });
69
- }
70
- }
71
-
72
- // Server options with typings
73
- const serverOptions: ServeOptions = {
74
- port: PORT,
75
- fetch(req: Request): Response | Promise<Response> {
76
- const url = new URL(req.url);
77
- const path = url.pathname === '/' ? '/' : url.pathname;
78
-
79
- return serveFile(path);
80
- },
81
- };
82
-
83
- // Create and start the server
84
- const server = serve(serverOptions);
85
-
86
- console.log(`Server running at http://localhost:${PORT}`);
@@ -1,318 +0,0 @@
1
- // src/components/slider/features/slider.ts
2
- import { SLIDER_EVENTS } from '../constants';
3
- import { SliderConfig } from '../types';
4
- import { createUiHelpers } from './ui';
5
- import { createHandlers } from './handlers';
6
-
7
- /**
8
- * Add main slider functionality to component
9
- * @param config Slider configuration
10
- * @returns Component enhancer with slider functionality
11
- */
12
- export const withSlider = (config: SliderConfig) => component => {
13
- // Ensure component has events
14
- if (!component.events) {
15
- component.events = {
16
- listeners: {},
17
- on(event, handler) {
18
- if (!this.listeners[event]) this.listeners[event] = [];
19
- this.listeners[event].push(handler);
20
- return this;
21
- },
22
- off(event, handler) {
23
- if (this.listeners[event]) {
24
- this.listeners[event] = this.listeners[event].filter(h => h !== handler);
25
- }
26
- return this;
27
- },
28
- trigger(event, data) {
29
- if (this.listeners[event]) {
30
- this.listeners[event].forEach(handler => handler(data));
31
- }
32
- return this;
33
- }
34
- };
35
- }
36
-
37
- // Initialize state
38
- const state = {
39
- value: config.value !== undefined ? config.value : 0,
40
- secondValue: config.secondValue !== undefined ? config.secondValue : null,
41
- min: config.min !== undefined ? config.min : 0,
42
- max: config.max !== undefined ? config.max : 100,
43
- step: config.step !== undefined ? config.step : 1,
44
- dragging: false,
45
- activeBubble: null,
46
- activeHandle: null,
47
- ticks: [],
48
- valueHideTimer: null,
49
- component
50
- };
51
-
52
- // Create event helpers
53
- const eventHelpers = {
54
- triggerEvent(eventName, originalEvent = null) {
55
- const eventData = {
56
- slider: state.component,
57
- value: state.value,
58
- secondValue: state.secondValue,
59
- originalEvent,
60
- preventDefault: () => { eventData.defaultPrevented = true; },
61
- defaultPrevented: false
62
- };
63
-
64
- state.component.events.trigger(eventName, eventData);
65
- return eventData;
66
- }
67
- };
68
-
69
- // Create UI helpers and handlers
70
- const uiHelpers = createUiHelpers(config, state);
71
- const handlers = createHandlers(config, state, uiHelpers, eventHelpers);
72
-
73
- // Initialize slider
74
- const initSlider = () => {
75
- // Set ARIA attributes
76
- component.element.setAttribute('aria-valuemin', String(state.min));
77
- component.element.setAttribute('aria-valuemax', String(state.max));
78
- component.element.setAttribute('aria-valuenow', String(state.value));
79
-
80
- if (!component.structure) {
81
- console.warn('Cannot initialize slider: missing structure');
82
- return;
83
- }
84
-
85
- const { handle, secondHandle } = component.structure;
86
-
87
- if (handle) {
88
- handle.setAttribute('aria-valuemin', String(state.min));
89
- handle.setAttribute('aria-valuemax', String(state.max));
90
- handle.setAttribute('aria-valuenow', String(state.value));
91
-
92
- if (config.range && secondHandle && state.secondValue !== null) {
93
- secondHandle.setAttribute('aria-valuemin', String(state.min));
94
- secondHandle.setAttribute('aria-valuemax', String(state.max));
95
- secondHandle.setAttribute('aria-valuenow', String(state.secondValue));
96
- }
97
- }
98
-
99
- // Initial UI update
100
- uiHelpers.updateUi();
101
-
102
- // Generate ticks if needed
103
- if (config.ticks || config.tickLabels) {
104
- uiHelpers.generateTicks();
105
- }
106
-
107
- // Setup event listeners
108
- handlers.setupEventListeners();
109
-
110
- // Force one more UI update after a delay to ensure proper positioning
111
- setTimeout(() => {
112
- uiHelpers.updateUi();
113
- }, 50);
114
- };
115
-
116
- // Register with lifecycle if available
117
- if (component.lifecycle) {
118
- const originalDestroy = component.lifecycle.destroy || (() => {});
119
- component.lifecycle.destroy = () => {
120
- handlers.cleanupEventListeners();
121
- originalDestroy();
122
- };
123
- }
124
-
125
- // Initialize slider
126
- initSlider();
127
-
128
- // Return enhanced component
129
- return {
130
- ...component,
131
- slider: {
132
- /**
133
- * Sets slider value
134
- * @param value New value
135
- * @param triggerEvent Whether to trigger change event
136
- * @returns Slider controller for chaining
137
- */
138
- setValue(value, triggerEvent = true) {
139
- const newValue = uiHelpers.clamp(value, state.min, state.max);
140
- state.value = newValue;
141
- uiHelpers.updateUi();
142
-
143
- if (triggerEvent) {
144
- eventHelpers.triggerEvent(SLIDER_EVENTS.CHANGE);
145
- }
146
-
147
- return this;
148
- },
149
-
150
- /**
151
- * Gets slider value
152
- * @returns Current value
153
- */
154
- getValue() {
155
- return state.value;
156
- },
157
-
158
- /**
159
- * Sets secondary slider value (for range slider)
160
- * @param value New secondary value
161
- * @param triggerEvent Whether to trigger change event
162
- * @returns Slider controller for chaining
163
- */
164
- setSecondValue(value, triggerEvent = true) {
165
- if (!config.range) return this;
166
-
167
- const newValue = uiHelpers.clamp(value, state.min, state.max);
168
- state.secondValue = newValue;
169
- uiHelpers.updateUi();
170
-
171
- if (triggerEvent) {
172
- eventHelpers.triggerEvent(SLIDER_EVENTS.CHANGE);
173
- }
174
-
175
- return this;
176
- },
177
-
178
- /**
179
- * Gets secondary slider value
180
- * @returns Current secondary value or null
181
- */
182
- getSecondValue() {
183
- return config.range ? state.secondValue : null;
184
- },
185
-
186
- /**
187
- * Sets slider minimum value
188
- * @param min New minimum value
189
- * @returns Slider controller for chaining
190
- */
191
- setMin(min) {
192
- state.min = min;
193
-
194
- // Update ARIA attributes
195
- component.element.setAttribute('aria-valuemin', String(min));
196
- if (component.structure.handle) {
197
- component.structure.handle.setAttribute('aria-valuemin', String(min));
198
- }
199
-
200
- if (config.range && component.structure.secondHandle) {
201
- component.structure.secondHandle.setAttribute('aria-valuemin', String(min));
202
- }
203
-
204
- // Clamp values to new min
205
- if (state.value < min) state.value = min;
206
- if (config.range && state.secondValue !== null && state.secondValue < min) {
207
- state.secondValue = min;
208
- }
209
-
210
- // Regenerate ticks if needed
211
- if (config.ticks || config.tickLabels) {
212
- uiHelpers.generateTicks();
213
- }
214
-
215
- uiHelpers.updateUi();
216
- return this;
217
- },
218
-
219
- /**
220
- * Gets slider minimum value
221
- * @returns Current minimum value
222
- */
223
- getMin() {
224
- return state.min;
225
- },
226
-
227
- /**
228
- * Sets slider maximum value
229
- * @param max New maximum value
230
- * @returns Slider controller for chaining
231
- */
232
- setMax(max) {
233
- state.max = max;
234
-
235
- // Update ARIA attributes
236
- component.element.setAttribute('aria-valuemax', String(max));
237
- if (component.structure.handle) {
238
- component.structure.handle.setAttribute('aria-valuemax', String(max));
239
- }
240
-
241
- if (config.range && component.structure.secondHandle) {
242
- component.structure.secondHandle.setAttribute('aria-valuemax', String(max));
243
- }
244
-
245
- // Clamp values to new max
246
- if (state.value > max) state.value = max;
247
- if (config.range && state.secondValue !== null && state.secondValue > max) {
248
- state.secondValue = max;
249
- }
250
-
251
- // Regenerate ticks if needed
252
- if (config.ticks || config.tickLabels) {
253
- uiHelpers.generateTicks();
254
- }
255
-
256
- uiHelpers.updateUi();
257
- return this;
258
- },
259
-
260
- /**
261
- * Gets slider maximum value
262
- * @returns Current maximum value
263
- */
264
- getMax() {
265
- return state.max;
266
- },
267
-
268
- /**
269
- * Sets slider step size
270
- * @param step New step size
271
- * @returns Slider controller for chaining
272
- */
273
- setStep(step) {
274
- state.step = step;
275
-
276
- // Add or remove discrete class
277
- component.element.classList[step > 0 ? 'add' : 'remove'](
278
- `${component.getClass('slider')}--discrete`
279
- );
280
-
281
- // Regenerate ticks if needed
282
- if (config.ticks || config.tickLabels) {
283
- uiHelpers.generateTicks();
284
- uiHelpers.updateTicks();
285
- }
286
-
287
- return this;
288
- },
289
-
290
- /**
291
- * Gets slider step size
292
- * @returns Current step size
293
- */
294
- getStep() {
295
- return state.step;
296
- },
297
-
298
- /**
299
- * Regenerate tick marks and labels
300
- * @returns Slider controller for chaining
301
- */
302
- regenerateTicks() {
303
- uiHelpers.generateTicks();
304
- uiHelpers.updateTicks();
305
- return this;
306
- },
307
-
308
- /**
309
- * Update all UI elements
310
- * @returns Slider controller for chaining
311
- */
312
- updateUi() {
313
- uiHelpers.updateUi();
314
- return this;
315
- }
316
- }
317
- };
318
- };