mtrl 0.2.2 → 0.2.4

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 (97) hide show
  1. package/.typedocignore +11 -0
  2. package/DOCS.md +153 -0
  3. package/index.ts +18 -3
  4. package/package.json +7 -2
  5. package/src/components/badge/_styles.scss +174 -0
  6. package/src/components/badge/api.ts +292 -0
  7. package/src/components/badge/badge.ts +52 -0
  8. package/src/components/badge/config.ts +68 -0
  9. package/src/components/badge/constants.ts +30 -0
  10. package/src/components/badge/features.ts +185 -0
  11. package/src/components/badge/index.ts +4 -0
  12. package/src/components/badge/types.ts +105 -0
  13. package/src/components/button/types.ts +174 -29
  14. package/src/components/carousel/_styles.scss +645 -0
  15. package/src/components/carousel/api.ts +147 -0
  16. package/src/components/carousel/carousel.ts +178 -0
  17. package/src/components/carousel/config.ts +91 -0
  18. package/src/components/carousel/constants.ts +95 -0
  19. package/src/components/carousel/features/drag.ts +388 -0
  20. package/src/components/carousel/features/index.ts +8 -0
  21. package/src/components/carousel/features/slides.ts +682 -0
  22. package/src/components/carousel/index.ts +38 -0
  23. package/src/components/carousel/types.ts +327 -0
  24. package/src/components/dialog/_styles.scss +213 -0
  25. package/src/components/dialog/api.ts +283 -0
  26. package/src/components/dialog/config.ts +113 -0
  27. package/src/components/dialog/constants.ts +32 -0
  28. package/src/components/dialog/dialog.ts +56 -0
  29. package/src/components/dialog/features.ts +713 -0
  30. package/src/components/dialog/index.ts +15 -0
  31. package/src/components/dialog/types.ts +221 -0
  32. package/src/components/progress/_styles.scss +13 -1
  33. package/src/components/progress/api.ts +2 -2
  34. package/src/components/progress/progress.ts +2 -2
  35. package/src/components/progress/types.ts +3 -0
  36. package/src/components/radios/_styles.scss +232 -0
  37. package/src/components/radios/api.ts +100 -0
  38. package/src/components/radios/config.ts +60 -0
  39. package/src/components/radios/constants.ts +28 -0
  40. package/src/components/radios/index.ts +4 -0
  41. package/src/components/radios/radio.ts +269 -0
  42. package/src/components/radios/radios.ts +42 -0
  43. package/src/components/radios/types.ts +232 -0
  44. package/src/components/sheet/_styles.scss +236 -0
  45. package/src/components/sheet/api.ts +96 -0
  46. package/src/components/sheet/config.ts +66 -0
  47. package/src/components/sheet/constants.ts +20 -0
  48. package/src/components/sheet/features/content.ts +51 -0
  49. package/src/components/sheet/features/gestures.ts +177 -0
  50. package/src/components/sheet/features/index.ts +6 -0
  51. package/src/components/sheet/features/position.ts +42 -0
  52. package/src/components/sheet/features/state.ts +116 -0
  53. package/src/components/sheet/features/title.ts +86 -0
  54. package/src/components/sheet/index.ts +4 -0
  55. package/src/components/sheet/sheet.ts +57 -0
  56. package/src/components/sheet/types.ts +266 -0
  57. package/src/components/slider/_styles.scss +518 -0
  58. package/src/components/slider/api.ts +336 -0
  59. package/src/components/slider/config.ts +145 -0
  60. package/src/components/slider/constants.ts +28 -0
  61. package/src/components/slider/features/appearance.ts +140 -0
  62. package/src/components/slider/features/disabled.ts +43 -0
  63. package/src/components/slider/features/events.ts +164 -0
  64. package/src/components/slider/features/index.ts +5 -0
  65. package/src/components/slider/features/interactions.ts +256 -0
  66. package/src/components/slider/features/keyboard.ts +114 -0
  67. package/src/components/slider/features/slider.ts +336 -0
  68. package/src/components/slider/features/structure.ts +264 -0
  69. package/src/components/slider/features/ui.ts +518 -0
  70. package/src/components/slider/index.ts +9 -0
  71. package/src/components/slider/slider.ts +58 -0
  72. package/src/components/slider/types.ts +166 -0
  73. package/src/components/tabs/_styles.scss +224 -0
  74. package/src/components/tabs/api.ts +443 -0
  75. package/src/components/tabs/config.ts +80 -0
  76. package/src/components/tabs/constants.ts +12 -0
  77. package/src/components/tabs/index.ts +4 -0
  78. package/src/components/tabs/tabs.ts +52 -0
  79. package/src/components/tabs/types.ts +247 -0
  80. package/src/components/textfield/_styles.scss +97 -4
  81. package/src/components/tooltip/_styles.scss +241 -0
  82. package/src/components/tooltip/api.ts +411 -0
  83. package/src/components/tooltip/config.ts +78 -0
  84. package/src/components/tooltip/constants.ts +27 -0
  85. package/src/components/tooltip/index.ts +4 -0
  86. package/src/components/tooltip/tooltip.ts +60 -0
  87. package/src/components/tooltip/types.ts +178 -0
  88. package/src/core/build/_ripple.scss +79 -0
  89. package/src/core/build/constants.ts +48 -0
  90. package/src/core/build/icon.ts +137 -0
  91. package/src/core/build/ripple.ts +216 -0
  92. package/src/core/build/text.ts +91 -0
  93. package/src/index.ts +9 -1
  94. package/src/styles/abstract/_variables.scss +24 -12
  95. package/tsconfig.json +22 -0
  96. package/typedoc.json +28 -0
  97. package/typedoc.simple.json +14 -0
@@ -0,0 +1,236 @@
1
+ // src/components/sheet/_styles.scss
2
+ @use '../../styles/abstract/base' as base;
3
+ @use '../../styles/abstract/variables' as v;
4
+ @use '../../styles/abstract/functions' as f;
5
+ @use '../../styles/abstract/mixins' as m;
6
+ @use '../../styles/abstract/theme' as t;
7
+
8
+ $component: '#{base.$prefix}-sheet';
9
+
10
+ .#{$component} {
11
+ // Base styles
12
+ position: fixed;
13
+ z-index: 1000;
14
+ display: flex;
15
+ flex-direction: column;
16
+ background-color: t.color('surface');
17
+ transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
18
+ overflow: hidden;
19
+
20
+ // Common elevated surface styling
21
+ border-radius: 12px 12px 0 0;
22
+ box-sizing: border-box;
23
+ max-width: 100%;
24
+ max-height: 100%;
25
+
26
+ // Initially hidden
27
+ opacity: 0;
28
+ pointer-events: none;
29
+
30
+ // Typography for content
31
+ @include m.typography('body-medium');
32
+
33
+ // Scrim (background overlay)
34
+ &-scrim {
35
+ position: fixed;
36
+ top: 0;
37
+ left: 0;
38
+ right: 0;
39
+ bottom: 0;
40
+ background-color: t.alpha('scrim', 0.32);
41
+ z-index: 999;
42
+ opacity: 0;
43
+ transition: opacity 0.3s ease-in-out;
44
+ pointer-events: none;
45
+ }
46
+
47
+ &--open {
48
+ opacity: 1;
49
+ pointer-events: auto;
50
+
51
+ + .#{$component}-scrim {
52
+ opacity: 1;
53
+ pointer-events: auto;
54
+ }
55
+ }
56
+
57
+ // Drag handle
58
+ &-handle {
59
+ width: 32px;
60
+ height: 4px;
61
+ border-radius: 2px;
62
+ background-color: t.color('outline-variant');
63
+ margin: 8px auto;
64
+ cursor: grab;
65
+
66
+ &:active {
67
+ cursor: grabbing;
68
+ }
69
+ }
70
+
71
+ // Title area
72
+ &-title {
73
+ padding: 16px 24px 0;
74
+ @include m.typography('headline-small');
75
+ color: t.color('on-surface');
76
+ }
77
+
78
+ // Content container
79
+ &-content {
80
+ padding: 16px 24px 24px;
81
+ overflow-y: auto;
82
+ flex: 1;
83
+ // Ensure a min-height for small content
84
+ min-height: 32px;
85
+ }
86
+
87
+ // Position variants
88
+ &--bottom {
89
+ bottom: 0;
90
+ left: 0;
91
+ right: 0;
92
+ transform: translateY(100%);
93
+ border-radius: 28px 28px 0 0;
94
+
95
+ &.#{$component}--open {
96
+ transform: translateY(0);
97
+ }
98
+ }
99
+
100
+ &--top {
101
+ top: 0;
102
+ left: 0;
103
+ right: 0;
104
+ transform: translateY(-100%);
105
+ border-radius: 0 0 28px 28px;
106
+
107
+ &.#{$component}--open {
108
+ transform: translateY(0);
109
+ }
110
+
111
+ .#{$component}-handle {
112
+ margin: 0 auto 8px;
113
+ }
114
+ }
115
+
116
+ &--left {
117
+ top: 0;
118
+ bottom: 0;
119
+ left: 0;
120
+ transform: translateX(-100%);
121
+ border-radius: 0 28px 28px 0;
122
+
123
+ &.#{$component}--open {
124
+ transform: translateX(0);
125
+ }
126
+
127
+ .#{$component}-handle {
128
+ width: 4px;
129
+ height: 32px;
130
+ margin: auto 8px auto auto;
131
+ }
132
+ }
133
+
134
+ &--right {
135
+ top: 0;
136
+ bottom: 0;
137
+ right: 0;
138
+ transform: translateX(100%);
139
+ border-radius: 28px 0 0 28px;
140
+
141
+ &.#{$component}--open {
142
+ transform: translateX(0);
143
+ }
144
+
145
+ .#{$component}-handle {
146
+ width: 4px;
147
+ height: 32px;
148
+ margin: auto auto auto 8px;
149
+ }
150
+ }
151
+
152
+ // Variant styles
153
+ &--standard {
154
+ // Apply different elevation based on position
155
+ &.#{$component}--bottom,
156
+ &.#{$component}--top {
157
+ width: 100%;
158
+ max-width: 640px;
159
+ margin: 0 auto;
160
+ @include m.elevation(3);
161
+ }
162
+
163
+ &.#{$component}--left,
164
+ &.#{$component}--right {
165
+ max-width: 360px;
166
+ width: 90%;
167
+ @include m.elevation(3);
168
+ }
169
+ }
170
+
171
+ &--modal {
172
+ // Modal variant has higher elevation and a required scrim
173
+ @include m.elevation(5);
174
+
175
+ &.#{$component}--bottom,
176
+ &.#{$component}--top {
177
+ width: 100%;
178
+ max-width: 560px;
179
+ margin: 0 auto;
180
+ }
181
+
182
+ &.#{$component}--left,
183
+ &.#{$component}--right {
184
+ max-width: 320px;
185
+ width: 90%;
186
+ }
187
+
188
+ + .#{$component}-scrim {
189
+ background-color: t.alpha('scrim', 0.5);
190
+ }
191
+ }
192
+
193
+ &--expanded {
194
+ // Full screen variant
195
+ &.#{$component}--bottom,
196
+ &.#{$component}--top {
197
+ height: 100%;
198
+ max-height: calc(100% - 24px);
199
+ width: 100%;
200
+ }
201
+
202
+ &.#{$component}--left,
203
+ &.#{$component}--right {
204
+ width: 100%;
205
+ max-width: 100%;
206
+ }
207
+ }
208
+
209
+ // States
210
+ &--dismissible {
211
+ .#{$component}-scrim {
212
+ cursor: pointer;
213
+ }
214
+ }
215
+
216
+ // Elevation levels (1-5)
217
+ &--elevation-1 {
218
+ @include m.elevation(1);
219
+ }
220
+
221
+ &--elevation-2 {
222
+ @include m.elevation(2);
223
+ }
224
+
225
+ &--elevation-3 {
226
+ @include m.elevation(3);
227
+ }
228
+
229
+ &--elevation-4 {
230
+ @include m.elevation(4);
231
+ }
232
+
233
+ &--elevation-5 {
234
+ @include m.elevation(5);
235
+ }
236
+ }
@@ -0,0 +1,96 @@
1
+ // src/components/sheet/api.ts
2
+ import { SheetComponent } from './types';
3
+
4
+ interface ApiOptions {
5
+ state: {
6
+ open: () => void;
7
+ close: () => void;
8
+ };
9
+ lifecycle: {
10
+ destroy: () => void;
11
+ };
12
+ }
13
+
14
+ interface ComponentWithElements {
15
+ element: HTMLElement;
16
+ container: HTMLElement;
17
+ content: {
18
+ setContent: (html: string) => any;
19
+ getContent: () => string;
20
+ getElement: () => HTMLElement | null;
21
+ };
22
+ title: {
23
+ setTitle: (text: string) => any;
24
+ getTitle: () => string;
25
+ getElement: () => HTMLElement | null;
26
+ };
27
+ getClass: (name: string) => string;
28
+ dragHandle?: {
29
+ setVisible: (visible: boolean) => any;
30
+ };
31
+ initialize?: () => void;
32
+ }
33
+
34
+ /**
35
+ * Enhances a sheet component with API methods
36
+ * @param {ApiOptions} options - API configuration options
37
+ * @returns {Function} Higher-order function that adds API methods to component
38
+ * @internal This is an internal utility for the Sheet component
39
+ */
40
+ export const withAPI = ({ state, lifecycle }: ApiOptions) =>
41
+ (component: ComponentWithElements): SheetComponent => ({
42
+ ...component as any,
43
+ element: component.element,
44
+ container: component.container,
45
+
46
+ open() {
47
+ state.open();
48
+ return this;
49
+ },
50
+
51
+ close() {
52
+ state.close();
53
+ return this;
54
+ },
55
+
56
+ setContent(html: string) {
57
+ component.content.setContent(html);
58
+ return this;
59
+ },
60
+
61
+ getContent() {
62
+ return component.content.getContent();
63
+ },
64
+
65
+ setTitle(text: string) {
66
+ component.title.setTitle(text);
67
+ return this;
68
+ },
69
+
70
+ getTitle() {
71
+ return component.title.getTitle();
72
+ },
73
+
74
+ setDragHandle(enabled: boolean) {
75
+ if (component.dragHandle) {
76
+ component.dragHandle.setVisible(enabled);
77
+ }
78
+ return this;
79
+ },
80
+
81
+ setMaxHeight(height: string) {
82
+ component.container.style.maxHeight = height;
83
+ return this;
84
+ },
85
+
86
+ destroy() {
87
+ lifecycle.destroy();
88
+ },
89
+
90
+ initialize() {
91
+ if (component.initialize) {
92
+ component.initialize();
93
+ }
94
+ return this;
95
+ }
96
+ });
@@ -0,0 +1,66 @@
1
+ // src/components/sheet/config.ts
2
+ import {
3
+ createComponentConfig,
4
+ createElementConfig,
5
+ BaseComponentConfig
6
+ } from '../../core/config/component-config';
7
+ import { SheetConfig } from './types';
8
+ import { SHEET_VARIANTS, SHEET_POSITIONS } from './constants';
9
+
10
+ /**
11
+ * Default configuration for the Sheet component
12
+ */
13
+ export const defaultConfig: SheetConfig = {
14
+ variant: SHEET_VARIANTS.STANDARD,
15
+ position: SHEET_POSITIONS.BOTTOM,
16
+ open: false,
17
+ dismissible: true,
18
+ dragHandle: true,
19
+ elevation: 3,
20
+ enableGestures: true
21
+ };
22
+
23
+ /**
24
+ * Creates the base configuration for Sheet component
25
+ * @param {SheetConfig} config - User provided configuration
26
+ * @returns {SheetConfig} Complete configuration with defaults applied
27
+ */
28
+ export const createBaseConfig = (config: SheetConfig = {}): SheetConfig =>
29
+ createComponentConfig(defaultConfig, config, 'sheet') as SheetConfig;
30
+
31
+ /**
32
+ * Generates element configuration for the Sheet component
33
+ * @param {SheetConfig} config - Sheet configuration
34
+ * @returns {Object} Element configuration object for withElement
35
+ */
36
+ export const getElementConfig = (config: SheetConfig) => {
37
+ return createElementConfig(config, {
38
+ tag: 'div',
39
+ attrs: {
40
+ role: 'dialog',
41
+ 'aria-modal': config.variant === SHEET_VARIANTS.MODAL ? 'true' : 'false'
42
+ },
43
+ className: config.class,
44
+ forwardEvents: {
45
+ click: true,
46
+ keydown: true
47
+ }
48
+ });
49
+ };
50
+
51
+ /**
52
+ * Creates API configuration for the Sheet component
53
+ * @param {Object} comp - Component with state and lifecycle features
54
+ * @returns {Object} API configuration object
55
+ */
56
+ export const getApiConfig = (comp) => ({
57
+ state: {
58
+ open: () => comp.state.open(),
59
+ close: () => comp.state.close()
60
+ },
61
+ lifecycle: {
62
+ destroy: () => comp.lifecycle.destroy()
63
+ }
64
+ });
65
+
66
+ export default defaultConfig;
@@ -0,0 +1,20 @@
1
+ // src/components/sheet/constants.ts
2
+ export const SHEET_VARIANTS = {
3
+ STANDARD: 'standard',
4
+ MODAL: 'modal',
5
+ EXPANDED: 'expanded'
6
+ }
7
+
8
+ export const SHEET_POSITIONS = {
9
+ BOTTOM: 'bottom',
10
+ TOP: 'top',
11
+ LEFT: 'left',
12
+ RIGHT: 'right'
13
+ }
14
+
15
+ export const SHEET_EVENTS = {
16
+ OPEN: 'open',
17
+ CLOSE: 'close',
18
+ DRAG_START: 'dragstart',
19
+ DRAG_END: 'dragend'
20
+ }
@@ -0,0 +1,51 @@
1
+ // src/components/sheet/features/content.ts
2
+ /**
3
+ * Adds content management functionality to a component
4
+ * @param {Object} config - Component configuration with optional content
5
+ * @returns {Function} Higher-order function that adds content to a component
6
+ */
7
+ export const withContent = (config) => (component) => {
8
+ const { content = '' } = config;
9
+
10
+ // Create content element
11
+ const contentElement = document.createElement('div');
12
+ contentElement.className = `${component.getClass('sheet')}-content`;
13
+ contentElement.innerHTML = content;
14
+
15
+ // Add content element to component
16
+ component.container = document.createElement('div');
17
+ component.container.className = `${component.getClass('sheet')}-container`;
18
+ component.container.appendChild(contentElement);
19
+ component.element.appendChild(component.container);
20
+
21
+ return {
22
+ ...component,
23
+ content: {
24
+ /**
25
+ * Sets the HTML content
26
+ * @param {string} html - HTML content
27
+ * @returns {Object} Content API for chaining
28
+ */
29
+ setContent(html: string) {
30
+ contentElement.innerHTML = html;
31
+ return this;
32
+ },
33
+
34
+ /**
35
+ * Gets the current HTML content
36
+ * @returns {string} HTML content
37
+ */
38
+ getContent() {
39
+ return contentElement.innerHTML;
40
+ },
41
+
42
+ /**
43
+ * Gets the content DOM element
44
+ * @returns {HTMLElement} Content element
45
+ */
46
+ getElement() {
47
+ return contentElement;
48
+ }
49
+ }
50
+ };
51
+ };
@@ -0,0 +1,177 @@
1
+ // src/components/sheet/features/gestures.ts
2
+ import { SHEET_EVENTS, SHEET_POSITIONS } from '../constants';
3
+
4
+ /**
5
+ * Adds gesture support to a component
6
+ * @param {Object} config - Component configuration
7
+ * @returns {Function} Higher-order function that adds gestures to a component
8
+ */
9
+ export const withGestures = (config) => (component) => {
10
+ // Skip if gestures are disabled
11
+ if (config.enableGestures === false) {
12
+ return component;
13
+ }
14
+
15
+ const position = config.position || SHEET_POSITIONS.BOTTOM;
16
+ let startY = 0;
17
+ let startX = 0;
18
+ let startTransform = 0;
19
+ let isDragging = false;
20
+
21
+ // Find drag handle if exists
22
+ const dragHandle = component.dragHandle?.element || component.element;
23
+
24
+ /**
25
+ * Handles the start of a drag gesture
26
+ * @param {TouchEvent|MouseEvent} event - The event object
27
+ */
28
+ const handleDragStart = (event) => {
29
+ if (!component.state.isOpen()) return;
30
+
31
+ isDragging = true;
32
+ document.body.style.userSelect = 'none';
33
+
34
+ // Get start position
35
+ if (event.type === 'touchstart') {
36
+ startY = event.touches[0].clientY;
37
+ startX = event.touches[0].clientX;
38
+ } else {
39
+ startY = event.clientY;
40
+ startX = event.clientX;
41
+
42
+ // Add mouse move and up listeners
43
+ document.addEventListener('mousemove', handleDragMove);
44
+ document.addEventListener('mouseup', handleDragEnd);
45
+ }
46
+
47
+ // Emit event
48
+ component.events.emit(SHEET_EVENTS.DRAG_START);
49
+ };
50
+
51
+ /**
52
+ * Handles the drag movement
53
+ * @param {TouchEvent|MouseEvent} event - The event object
54
+ */
55
+ const handleDragMove = (event) => {
56
+ if (!isDragging) return;
57
+
58
+ let currentY, currentX;
59
+
60
+ if (event.type === 'touchmove') {
61
+ currentY = event.touches[0].clientY;
62
+ currentX = event.touches[0].clientX;
63
+ } else {
64
+ currentY = event.clientY;
65
+ currentX = event.clientX;
66
+ }
67
+
68
+ // Calculate delta based on position
69
+ let delta = 0;
70
+
71
+ switch (position) {
72
+ case SHEET_POSITIONS.BOTTOM:
73
+ delta = currentY - startY;
74
+ if (delta < 0) delta = 0; // Prevent dragging beyond fully open state
75
+ component.element.style.transform = `translateY(${delta}px)`;
76
+ break;
77
+
78
+ case SHEET_POSITIONS.TOP:
79
+ delta = startY - currentY;
80
+ if (delta < 0) delta = 0;
81
+ component.element.style.transform = `translateY(-${delta}px)`;
82
+ break;
83
+
84
+ case SHEET_POSITIONS.LEFT:
85
+ delta = startX - currentX;
86
+ if (delta < 0) delta = 0;
87
+ component.element.style.transform = `translateX(-${delta}px)`;
88
+ break;
89
+
90
+ case SHEET_POSITIONS.RIGHT:
91
+ delta = currentX - startX;
92
+ if (delta < 0) delta = 0;
93
+ component.element.style.transform = `translateX(${delta}px)`;
94
+ break;
95
+ }
96
+
97
+ // Adjust opacity based on drag progress
98
+ const elementSize = position === SHEET_POSITIONS.BOTTOM || position === SHEET_POSITIONS.TOP
99
+ ? component.element.offsetHeight
100
+ : component.element.offsetWidth;
101
+
102
+ const dragProgress = Math.min(delta / elementSize, 1);
103
+ const scrimElement = component.element.nextElementSibling;
104
+ if (scrimElement && scrimElement.classList.contains(`${component.getClass('sheet')}-scrim`)) {
105
+ scrimElement.style.opacity = `${0.32 * (1 - dragProgress)}`;
106
+ }
107
+ };
108
+
109
+ /**
110
+ * Handles the end of a drag gesture
111
+ * @param {TouchEvent|MouseEvent} event - The event object
112
+ */
113
+ const handleDragEnd = (event) => {
114
+ if (!isDragging) return;
115
+
116
+ isDragging = false;
117
+ document.body.style.userSelect = '';
118
+
119
+ // Remove mouse event listeners
120
+ document.removeEventListener('mousemove', handleDragMove);
121
+ document.removeEventListener('mouseup', handleDragEnd);
122
+
123
+ // Determine final position
124
+ const elementSize = position === SHEET_POSITIONS.BOTTOM || position === SHEET_POSITIONS.TOP
125
+ ? component.element.offsetHeight
126
+ : component.element.offsetWidth;
127
+
128
+ let transform = 0;
129
+ if (component.element.style.transform) {
130
+ const match = component.element.style.transform.match(/translate[XY]\(([^p]+)px\)/);
131
+ transform = match ? parseFloat(match[1]) : 0;
132
+ }
133
+
134
+ // Close if dragged more than 30% of the way
135
+ if (transform > elementSize * 0.3) {
136
+ component.state.close();
137
+ } else {
138
+ // Reset position and opacity
139
+ component.element.style.transform = '';
140
+ const scrimElement = component.element.nextElementSibling;
141
+ if (scrimElement && scrimElement.classList.contains(`${component.getClass('sheet')}-scrim`)) {
142
+ scrimElement.style.opacity = '';
143
+ }
144
+ }
145
+
146
+ // Emit event
147
+ component.events.emit(SHEET_EVENTS.DRAG_END);
148
+ };
149
+
150
+ // Add event listeners to drag handle
151
+ dragHandle.addEventListener('touchstart', handleDragStart);
152
+ dragHandle.addEventListener('touchmove', handleDragMove);
153
+ dragHandle.addEventListener('touchend', handleDragEnd);
154
+ dragHandle.addEventListener('mousedown', handleDragStart);
155
+
156
+ // Clean up function
157
+ const cleanup = () => {
158
+ dragHandle.removeEventListener('touchstart', handleDragStart);
159
+ dragHandle.removeEventListener('touchmove', handleDragMove);
160
+ dragHandle.removeEventListener('touchend', handleDragEnd);
161
+ dragHandle.removeEventListener('mousedown', handleDragStart);
162
+ document.removeEventListener('mousemove', handleDragMove);
163
+ document.removeEventListener('mouseup', handleDragEnd);
164
+ };
165
+
166
+ // Add cleanup to component lifecycle
167
+ const originalDestroy = component.lifecycle?.destroy || (() => {});
168
+ component.lifecycle = {
169
+ ...component.lifecycle,
170
+ destroy: () => {
171
+ cleanup();
172
+ originalDestroy();
173
+ }
174
+ };
175
+
176
+ return component;
177
+ };
@@ -0,0 +1,6 @@
1
+ // src/components/sheet/features/index.ts
2
+ export { withPosition } from './position';
3
+ export { withContent } from './content';
4
+ export { withTitle } from './title';
5
+ export { withState } from './state';
6
+ export { withGestures } from './gestures';
@@ -0,0 +1,42 @@
1
+ // src/components/sheet/features/position.ts
2
+ import { SheetConfig } from '../types';
3
+ import { SHEET_POSITIONS } from '../constants';
4
+
5
+ /**
6
+ * Adds position functionality to a component
7
+ * @param {SheetConfig} config - Component configuration with position
8
+ * @returns {Function} Higher-order function that adds position to a component
9
+ */
10
+ export const withPosition = (config: SheetConfig) => (component) => {
11
+ const { position = SHEET_POSITIONS.BOTTOM } = config;
12
+ const positionClass = `${component.getClass('sheet')}--${position.toLowerCase()}`;
13
+
14
+ // Add position class to element
15
+ component.element.classList.add(positionClass);
16
+
17
+ return {
18
+ ...component,
19
+ position: {
20
+ /**
21
+ * Changes the position of the component
22
+ * @param {string} newPosition - New position value
23
+ */
24
+ setPosition(newPosition: string) {
25
+ // Remove current position class
26
+ component.element.classList.remove(positionClass);
27
+
28
+ // Add new position class
29
+ const newPositionClass = `${component.getClass('sheet')}--${newPosition.toLowerCase()}`;
30
+ component.element.classList.add(newPositionClass);
31
+ },
32
+
33
+ /**
34
+ * Gets the current position
35
+ * @returns {string} Current position
36
+ */
37
+ getPosition() {
38
+ return position;
39
+ }
40
+ }
41
+ };
42
+ };