mtrl 0.1.2 → 0.2.0

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 (220) hide show
  1. package/README.md +70 -22
  2. package/index.ts +33 -0
  3. package/package.json +14 -5
  4. package/src/components/button/{styles.scss → _styles.scss} +2 -2
  5. package/src/components/button/api.ts +89 -0
  6. package/src/components/button/button.ts +50 -0
  7. package/src/components/button/config.ts +75 -0
  8. package/src/components/button/constants.ts +17 -0
  9. package/src/components/button/index.ts +4 -0
  10. package/src/components/button/types.ts +118 -0
  11. package/src/components/card/_styles.scss +359 -0
  12. package/src/components/card/actions.ts +48 -0
  13. package/src/components/card/api.ts +102 -0
  14. package/src/components/card/card.ts +41 -0
  15. package/src/components/card/config.ts +99 -0
  16. package/src/components/card/constants.ts +69 -0
  17. package/src/components/card/content.ts +48 -0
  18. package/src/components/card/features.ts +228 -0
  19. package/src/components/card/header.ts +88 -0
  20. package/src/components/card/index.ts +19 -0
  21. package/src/components/card/media.ts +52 -0
  22. package/src/components/card/types.ts +174 -0
  23. package/src/components/checkbox/api.ts +82 -0
  24. package/src/components/checkbox/checkbox.ts +75 -0
  25. package/src/components/checkbox/config.ts +90 -0
  26. package/src/components/checkbox/index.ts +4 -0
  27. package/src/components/checkbox/types.ts +146 -0
  28. package/src/components/chip/_styles.scss +372 -0
  29. package/src/components/chip/api.ts +115 -0
  30. package/src/components/chip/chip-set.ts +225 -0
  31. package/src/components/chip/chip.ts +82 -0
  32. package/src/components/chip/config.ts +92 -0
  33. package/src/components/chip/constants.ts +38 -0
  34. package/src/components/chip/index.ts +4 -0
  35. package/src/components/chip/types.ts +172 -0
  36. package/src/components/list/api.ts +72 -0
  37. package/src/components/list/config.ts +43 -0
  38. package/src/components/list/{constants.js → constants.ts} +34 -7
  39. package/src/components/list/features.ts +224 -0
  40. package/src/components/list/index.ts +14 -0
  41. package/src/components/list/list-item.ts +120 -0
  42. package/src/components/list/list.ts +37 -0
  43. package/src/components/list/types.ts +179 -0
  44. package/src/components/list/utils.ts +47 -0
  45. package/src/components/menu/api.ts +119 -0
  46. package/src/components/menu/config.ts +54 -0
  47. package/src/components/menu/constants.ts +154 -0
  48. package/src/components/menu/features/items-manager.ts +457 -0
  49. package/src/components/menu/features/keyboard-navigation.ts +133 -0
  50. package/src/components/menu/features/positioning.ts +127 -0
  51. package/src/components/menu/features/{visibility.js → visibility.ts} +66 -64
  52. package/src/components/menu/index.ts +14 -0
  53. package/src/components/menu/menu-item.ts +43 -0
  54. package/src/components/menu/menu.ts +53 -0
  55. package/src/components/menu/types.ts +178 -0
  56. package/src/components/navigation/api.ts +79 -0
  57. package/src/components/navigation/config.ts +61 -0
  58. package/src/components/navigation/{constants.js → constants.ts} +10 -10
  59. package/src/components/navigation/index.ts +14 -0
  60. package/src/components/navigation/nav-item.ts +148 -0
  61. package/src/components/navigation/navigation.ts +50 -0
  62. package/src/components/navigation/types.ts +212 -0
  63. package/src/components/progress/_styles.scss +204 -0
  64. package/src/components/progress/api.ts +179 -0
  65. package/src/components/progress/config.ts +124 -0
  66. package/src/components/progress/constants.ts +43 -0
  67. package/src/components/progress/index.ts +5 -0
  68. package/src/components/progress/progress.ts +163 -0
  69. package/src/components/progress/types.ts +102 -0
  70. package/src/components/snackbar/api.ts +162 -0
  71. package/src/components/snackbar/config.ts +62 -0
  72. package/src/components/snackbar/{constants.js → constants.ts} +21 -4
  73. package/src/components/snackbar/features.ts +76 -0
  74. package/src/components/snackbar/index.ts +4 -0
  75. package/src/components/snackbar/position.ts +71 -0
  76. package/src/components/snackbar/queue.ts +76 -0
  77. package/src/components/snackbar/snackbar.ts +60 -0
  78. package/src/components/snackbar/types.ts +58 -0
  79. package/src/components/switch/api.ts +77 -0
  80. package/src/components/switch/config.ts +74 -0
  81. package/src/components/switch/index.ts +4 -0
  82. package/src/components/switch/switch.ts +52 -0
  83. package/src/components/switch/types.ts +142 -0
  84. package/src/components/textfield/api.ts +72 -0
  85. package/src/components/textfield/config.ts +54 -0
  86. package/src/components/textfield/{constants.js → constants.ts} +38 -5
  87. package/src/components/textfield/index.ts +4 -0
  88. package/src/components/textfield/textfield.ts +50 -0
  89. package/src/components/textfield/types.ts +139 -0
  90. package/src/core/compose/base.ts +43 -0
  91. package/src/core/compose/component.ts +247 -0
  92. package/src/core/compose/features/checkable.ts +155 -0
  93. package/src/core/compose/features/disabled.ts +116 -0
  94. package/src/core/compose/features/events.ts +65 -0
  95. package/src/core/compose/features/icon.ts +67 -0
  96. package/src/core/compose/features/index.ts +35 -0
  97. package/src/core/compose/features/input.ts +174 -0
  98. package/src/core/compose/features/lifecycle.ts +139 -0
  99. package/src/core/compose/features/position.ts +94 -0
  100. package/src/core/compose/features/ripple.ts +55 -0
  101. package/src/core/compose/features/size.ts +29 -0
  102. package/src/core/compose/features/style.ts +31 -0
  103. package/src/core/compose/features/text.ts +44 -0
  104. package/src/core/compose/features/textinput.ts +225 -0
  105. package/src/core/compose/features/textlabel.ts +92 -0
  106. package/src/core/compose/features/track.ts +84 -0
  107. package/src/core/compose/features/variant.ts +29 -0
  108. package/src/core/compose/features/withEvents.ts +137 -0
  109. package/src/core/compose/index.ts +54 -0
  110. package/src/core/compose/{pipe.js → pipe.ts} +16 -11
  111. package/src/core/config/component-config.ts +136 -0
  112. package/src/core/config.ts +211 -0
  113. package/src/core/dom/{attributes.js → attributes.ts} +11 -11
  114. package/src/core/dom/classes.ts +60 -0
  115. package/src/core/dom/create.ts +188 -0
  116. package/src/core/dom/events.ts +209 -0
  117. package/src/core/dom/index.ts +10 -0
  118. package/src/core/dom/utils.ts +97 -0
  119. package/src/core/index.ts +111 -0
  120. package/src/core/state/disabled.ts +81 -0
  121. package/src/core/state/emitter.ts +94 -0
  122. package/src/core/state/events.ts +88 -0
  123. package/src/core/state/index.ts +16 -0
  124. package/src/core/state/lifecycle.ts +131 -0
  125. package/src/core/state/store.ts +197 -0
  126. package/src/core/utils/index.ts +45 -0
  127. package/src/core/utils/{mobile.js → mobile.ts} +48 -24
  128. package/src/core/utils/object.ts +41 -0
  129. package/src/core/utils/validate.ts +234 -0
  130. package/src/{index.js → index.ts} +4 -2
  131. package/index.js +0 -11
  132. package/src/components/button/api.js +0 -54
  133. package/src/components/button/button.js +0 -81
  134. package/src/components/button/config.js +0 -10
  135. package/src/components/button/constants.js +0 -63
  136. package/src/components/button/index.js +0 -2
  137. package/src/components/checkbox/api.js +0 -45
  138. package/src/components/checkbox/checkbox.js +0 -96
  139. package/src/components/checkbox/index.js +0 -2
  140. package/src/components/container/api.js +0 -42
  141. package/src/components/container/container.js +0 -45
  142. package/src/components/container/index.js +0 -2
  143. package/src/components/container/styles.scss +0 -66
  144. package/src/components/list/index.js +0 -2
  145. package/src/components/list/list-item.js +0 -147
  146. package/src/components/list/list.js +0 -267
  147. package/src/components/menu/api.js +0 -117
  148. package/src/components/menu/constants.js +0 -42
  149. package/src/components/menu/features/items-manager.js +0 -375
  150. package/src/components/menu/features/keyboard-navigation.js +0 -129
  151. package/src/components/menu/features/positioning.js +0 -125
  152. package/src/components/menu/index.js +0 -2
  153. package/src/components/menu/menu-item.js +0 -41
  154. package/src/components/menu/menu.js +0 -54
  155. package/src/components/navigation/api.js +0 -43
  156. package/src/components/navigation/index.js +0 -2
  157. package/src/components/navigation/nav-item.js +0 -137
  158. package/src/components/navigation/navigation.js +0 -55
  159. package/src/components/snackbar/api.js +0 -125
  160. package/src/components/snackbar/features.js +0 -69
  161. package/src/components/snackbar/index.js +0 -2
  162. package/src/components/snackbar/position.js +0 -63
  163. package/src/components/snackbar/queue.js +0 -74
  164. package/src/components/snackbar/snackbar.js +0 -70
  165. package/src/components/switch/api.js +0 -44
  166. package/src/components/switch/index.js +0 -2
  167. package/src/components/switch/switch.js +0 -71
  168. package/src/components/textfield/api.js +0 -49
  169. package/src/components/textfield/index.js +0 -2
  170. package/src/components/textfield/textfield.js +0 -68
  171. package/src/core/build/_ripple.scss +0 -79
  172. package/src/core/build/constants.js +0 -51
  173. package/src/core/build/icon.js +0 -78
  174. package/src/core/build/ripple.js +0 -159
  175. package/src/core/build/text.js +0 -54
  176. package/src/core/compose/base.js +0 -8
  177. package/src/core/compose/component.js +0 -225
  178. package/src/core/compose/features/checkable.js +0 -114
  179. package/src/core/compose/features/disabled.js +0 -64
  180. package/src/core/compose/features/events.js +0 -48
  181. package/src/core/compose/features/icon.js +0 -33
  182. package/src/core/compose/features/index.js +0 -20
  183. package/src/core/compose/features/input.js +0 -100
  184. package/src/core/compose/features/lifecycle.js +0 -69
  185. package/src/core/compose/features/position.js +0 -60
  186. package/src/core/compose/features/ripple.js +0 -32
  187. package/src/core/compose/features/size.js +0 -9
  188. package/src/core/compose/features/style.js +0 -12
  189. package/src/core/compose/features/text.js +0 -17
  190. package/src/core/compose/features/textinput.js +0 -114
  191. package/src/core/compose/features/textlabel.js +0 -28
  192. package/src/core/compose/features/track.js +0 -49
  193. package/src/core/compose/features/variant.js +0 -9
  194. package/src/core/compose/features/withEvents.js +0 -67
  195. package/src/core/compose/index.js +0 -16
  196. package/src/core/config.js +0 -140
  197. package/src/core/dom/classes.js +0 -70
  198. package/src/core/dom/create.js +0 -132
  199. package/src/core/dom/events.js +0 -175
  200. package/src/core/dom/index.js +0 -5
  201. package/src/core/dom/utils.js +0 -22
  202. package/src/core/index.js +0 -23
  203. package/src/core/state/disabled.js +0 -51
  204. package/src/core/state/emitter.js +0 -63
  205. package/src/core/state/events.js +0 -29
  206. package/src/core/state/index.js +0 -6
  207. package/src/core/state/lifecycle.js +0 -64
  208. package/src/core/state/store.js +0 -112
  209. package/src/core/utils/index.js +0 -39
  210. package/src/core/utils/object.js +0 -22
  211. package/src/core/utils/validate.js +0 -37
  212. /package/src/components/checkbox/{styles.scss → _styles.scss} +0 -0
  213. /package/src/components/checkbox/{constants.js → constants.ts} +0 -0
  214. /package/src/components/list/{styles.scss → _styles.scss} +0 -0
  215. /package/src/components/menu/{styles.scss → _styles.scss} +0 -0
  216. /package/src/components/navigation/{styles.scss → _styles.scss} +0 -0
  217. /package/src/components/snackbar/{styles.scss → _styles.scss} +0 -0
  218. /package/src/components/switch/{styles.scss → _styles.scss} +0 -0
  219. /package/src/components/switch/{constants.js → constants.ts} +0 -0
  220. /package/src/components/textfield/{styles.scss → _styles.scss} +0 -0
@@ -1,54 +0,0 @@
1
- // src/components/menu/menu.js
2
-
3
- import { PREFIX } from '../../core/config'
4
- import { pipe } from '../../core/compose'
5
- import { createBase, withElement } from '../../core/compose/component'
6
- import { withEvents, withLifecycle } from '../../core/compose/features'
7
- import { withAPI } from './api'
8
- import { withVisibility } from './features/visibility'
9
- import { withItemsManager } from './features/items-manager'
10
- import { withPositioning } from './features/positioning'
11
- import { withKeyboardNavigation } from './features/keyboard-navigation'
12
-
13
- /**
14
- * Creates a new Menu component
15
- * @param {Object} config - Menu configuration
16
- * @param {Array} [config.items] - Menu items
17
- * @param {string} [config.class] - Additional CSS classes
18
- * @param {HTMLElement} [config.target] - Target element for positioning
19
- * @param {boolean} [config.stayOpenOnSelect] - Whether to keep the menu open after an item is selected
20
- * @param {HTMLElement} [config.openingButton] - Button that opens the menu
21
- * @returns {Object} Menu component instance
22
- */
23
- const createMenu = (config = {}) => {
24
- const baseConfig = {
25
- ...config,
26
- componentName: 'menu',
27
- prefix: PREFIX
28
- }
29
-
30
- return pipe(
31
- createBase,
32
- withEvents(),
33
- withElement({
34
- tag: 'div',
35
- componentName: 'menu',
36
- className: config.class,
37
- attrs: {
38
- role: 'menu',
39
- tabindex: '-1',
40
- 'aria-hidden': 'true'
41
- }
42
- }),
43
- withLifecycle(),
44
- withItemsManager(baseConfig),
45
- withVisibility(baseConfig),
46
- withPositioning,
47
- withKeyboardNavigation(baseConfig),
48
- comp => withAPI({
49
- lifecycle: comp.lifecycle
50
- })(comp)
51
- )(baseConfig)
52
- }
53
-
54
- export default createMenu
@@ -1,43 +0,0 @@
1
- // src/components/navigation/api.js
2
-
3
- /**
4
- * Enhances navigation component with API methods
5
- * @param {Object} options - API configuration
6
- * @param {Object} options.disabled - Disabled state handlers
7
- * @param {Object} options.lifecycle - Lifecycle handlers
8
- */
9
- export const withAPI = ({ disabled, lifecycle }) => (component) => ({
10
- ...component,
11
- element: component.element,
12
-
13
- // Item management
14
- addItem (config) {
15
- component.addItem?.(config)
16
- return this
17
- },
18
- removeItem (id) {
19
- component.removeItem?.(id)
20
- return this
21
- },
22
- getItem (id) {
23
- return component.getItem?.(id)
24
- },
25
-
26
- // Active state management
27
- setActive (id) {
28
- component.setActive?.(id)
29
- return this
30
- },
31
- getActive () {
32
- return component.getActive?.()
33
- },
34
-
35
- // Event handling
36
- on: component.on,
37
- off: component.off,
38
-
39
- // State management
40
- enable: disabled.enable,
41
- disable: disabled.disable,
42
- destroy: lifecycle.destroy
43
- })
@@ -1,2 +0,0 @@
1
- // src/components/navigation/index.js
2
- export { default } from './navigation.js'
@@ -1,137 +0,0 @@
1
- // src/components/navigation/nav-item.js
2
-
3
- /**
4
- * Creates an expand/collapse icon element
5
- * @param {string} prefix - CSS class prefix
6
- * @returns {HTMLElement} Expand icon element
7
- */
8
- const createExpandIcon = (prefix) => {
9
- const icon = document.createElement('span')
10
- icon.className = `${prefix}-nav-expand-icon`
11
- icon.innerHTML = `
12
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
13
- <polyline points="9 18 15 12 9 6"></polyline>
14
- </svg>
15
- `
16
- return icon
17
- }
18
-
19
- /**
20
- * Creates a nested items container
21
- * @param {Array} items - Nested items configuration
22
- * @param {string} prefix - CSS class prefix
23
- * @param {Function} createItem - Item creation function
24
- * @returns {HTMLElement} Nested items container
25
- */
26
- const createNestedContainer = (items, prefix, createItem) => {
27
- const container = document.createElement('div')
28
- container.className = `${prefix}-nav-nested-container`
29
- container.setAttribute('role', 'group')
30
- container.hidden = true
31
-
32
- items.forEach(itemConfig => {
33
- createItem(itemConfig, container, prefix)
34
- })
35
-
36
- return container
37
- }
38
-
39
- /**
40
- * Creates a navigation item element
41
- * @param {Object} config - Item configuration
42
- * @param {HTMLElement} container - Container element
43
- * @param {string} prefix - CSS class prefix
44
- * @returns {HTMLElement} Created navigation item
45
- */
46
- export const createNavItem = (config, container, prefix) => {
47
- const itemContainer = document.createElement('div')
48
- itemContainer.className = `${prefix}-nav-item-container`
49
-
50
- const item = document.createElement('button')
51
- item.className = `${prefix}-nav-item`
52
- item.setAttribute('role', config.items?.length ? 'button' : 'menuitem')
53
- item.setAttribute('aria-selected', 'false')
54
-
55
- if (config.id) {
56
- item.dataset.id = config.id
57
- }
58
-
59
- if (config.disabled) {
60
- item.disabled = true
61
- item.setAttribute('aria-disabled', 'true')
62
- }
63
-
64
- // Add icon if provided
65
- if (config.icon) {
66
- const icon = document.createElement('span')
67
- icon.className = `${prefix}-nav-item-icon`
68
- icon.innerHTML = config.icon
69
- item.appendChild(icon)
70
- }
71
-
72
- // Add label if provided
73
- if (config.label) {
74
- const label = document.createElement('span')
75
- label.className = `${prefix}-nav-item-label`
76
- label.textContent = config.label
77
- item.appendChild(label)
78
- item.setAttribute('aria-label', config.label)
79
- }
80
-
81
- // Add badge if provided
82
- if (config.badge) {
83
- const badge = document.createElement('span')
84
- badge.className = `${prefix}-nav-item-badge`
85
- badge.textContent = config.badge
86
- badge.setAttribute('aria-label', `${config.badge} notifications`)
87
- item.appendChild(badge)
88
- }
89
-
90
- itemContainer.appendChild(item)
91
-
92
- // Handle nested items - only for drawer variant
93
- if (config.items?.length && container.closest('.mtrl-nav--drawer, .mtrl-nav--drawer-modal, .mtrl-nav--drawer-standard')) {
94
- const expandIcon = createExpandIcon(prefix)
95
- item.appendChild(expandIcon)
96
-
97
- item.setAttribute('aria-expanded', config.expanded ? 'true' : 'false')
98
- item.setAttribute('aria-haspopup', 'true')
99
-
100
- const nestedContainer = createNestedContainer(config.items, prefix, createNavItem)
101
- nestedContainer.hidden = !config.expanded
102
- itemContainer.appendChild(nestedContainer)
103
-
104
- // Handle expand/collapse
105
- item.addEventListener('click', (event) => {
106
- event.stopPropagation()
107
- const isExpanded = item.getAttribute('aria-expanded') === 'true'
108
- item.setAttribute('aria-expanded', (!isExpanded).toString())
109
- nestedContainer.hidden = isExpanded
110
-
111
- // Toggle expand icon rotation
112
- expandIcon.style.transform = isExpanded ? '' : 'rotate(90deg)'
113
- })
114
- }
115
-
116
- container.appendChild(itemContainer)
117
- return item
118
- }
119
-
120
- /**
121
- * Recursively gets all nested items from a navigation item
122
- * @param {HTMLElement} item - Navigation item element
123
- * @param {string} prefix - CSS class prefix
124
- * @returns {Array<HTMLElement>} Array of all nested items
125
- */
126
- export const getAllNestedItems = (item, prefix) => {
127
- const container = item.closest(`.${prefix}-nav-item-container`)
128
- if (!container) return []
129
-
130
- const nestedContainer = container.querySelector(`.${prefix}-nav-nested-container`)
131
- if (!nestedContainer) return []
132
-
133
- const items = Array.from(nestedContainer.querySelectorAll(`.${prefix}-nav-item`))
134
- return items.reduce((acc, nestedItem) => {
135
- return [...acc, nestedItem, ...getAllNestedItems(nestedItem, prefix)]
136
- }, [])
137
- }
@@ -1,55 +0,0 @@
1
- // src/components/navigation/index.js
2
- import { PREFIX } from '../../core/config'
3
- import { pipe } from '../../core/compose'
4
- import { createBase, withElement } from '../../core/compose/component'
5
- import {
6
- withEvents,
7
- withDisabled,
8
- withLifecycle,
9
- withVariant,
10
- withPosition // Import core position feature
11
- } from '../../core/compose/features'
12
- import { withAPI } from './api'
13
- import { withNavItems } from './features/items'
14
-
15
- /**
16
- * Creates a new Navigation component
17
- * @param {Object} config - Navigation configuration
18
- * @param {string} [config.variant='rail'] - Navigation type (rail/drawer/bar)
19
- * @param {string} [config.position='left'] - Navigation position
20
- * @param {Array} [config.items=[]] - Navigation items
21
- * @param {boolean} [config.disabled=false] - Is navigation disabled
22
- * @param {string} [config.class] - Additional CSS classes
23
- */
24
- const createNavigation = (config = {}) => {
25
- const baseConfig = {
26
- ...config,
27
- componentName: 'nav',
28
- prefix: PREFIX
29
- }
30
-
31
- return pipe(
32
- createBase,
33
- // First add events system
34
- withEvents(),
35
- // Then add the element and other features
36
- withElement({
37
- tag: 'nav',
38
- role: 'navigation',
39
- 'aria-label': config.ariaLabel || 'Main Navigation',
40
- componentName: 'nav',
41
- className: config.class
42
- }),
43
- withVariant(baseConfig),
44
- withPosition(baseConfig),
45
- withNavItems(baseConfig),
46
- withDisabled(),
47
- withLifecycle(),
48
- comp => withAPI({
49
- disabled: comp.disabled,
50
- lifecycle: comp.lifecycle
51
- })(comp)
52
- )(baseConfig)
53
- }
54
-
55
- export default createNavigation
@@ -1,125 +0,0 @@
1
- // src/components/snackbar/api.js
2
-
3
- /**
4
- * Enhances snackbar component with API methods
5
- * @param {Object} options - API configuration
6
- * @param {Object} options.lifecycle - Lifecycle handlers
7
- * @param {Object} options.queue - Snackbar queue manager
8
- */
9
- export const withAPI = ({ lifecycle, queue }) => (component) => {
10
- if (!queue) {
11
- throw new Error('Snackbar queue is required')
12
- }
13
-
14
- let isVisible = false
15
-
16
- const enhancedComponent = {
17
- ...component,
18
- element: component.element,
19
-
20
- /**
21
- * Shows the snackbar with animation
22
- */
23
- show () {
24
- if (isVisible) return this
25
- isVisible = true
26
-
27
- queue.add({
28
- ...this,
29
- _show: () => {
30
- document.body.appendChild(component.element)
31
-
32
- // Force reflow for animation
33
- const _ = component.element.offsetHeight
34
-
35
- component.element.classList.add(`${component.getClass('snackbar')}--visible`)
36
-
37
- if (component.timer) {
38
- component.timer.start()
39
- }
40
-
41
- return this
42
- }
43
- })
44
-
45
- return this
46
- },
47
-
48
- /**
49
- * Hides the snackbar with animation and cleanup
50
- */
51
- hide () {
52
- if (!isVisible) return this
53
- isVisible = false
54
-
55
- if (component.timer) {
56
- component.timer.stop()
57
- }
58
-
59
- const handleTransitionEnd = (event) => {
60
- if (event.propertyName !== 'opacity') return
61
-
62
- component.element.removeEventListener('transitionend', handleTransitionEnd)
63
- if (component.element.parentNode) {
64
- component.element.remove()
65
- }
66
- }
67
-
68
- component.element.addEventListener('transitionend', handleTransitionEnd)
69
- component.element.classList.remove(`${component.getClass('snackbar')}--visible`)
70
-
71
- return this
72
- },
73
-
74
- setMessage (text) {
75
- component.text.setText(text)
76
- return this
77
- },
78
-
79
- getMessage () {
80
- return component.text.getText()
81
- },
82
-
83
- on: component.on,
84
- off: component.off,
85
-
86
- destroy () {
87
- if (isVisible) {
88
- component.element.remove()
89
- }
90
- if (component.timer) {
91
- component.timer.stop()
92
- }
93
- lifecycle.destroy()
94
- }
95
- }
96
-
97
- // Set up action button handler
98
- if (component.actionButton) {
99
- component.actionButton.addEventListener('click', () => {
100
- component.emit('action')
101
- component.emit('dismiss') // Emit dismiss to handle queue cleanup
102
- })
103
- }
104
-
105
- // Set up dismiss handler
106
- if (component.on) {
107
- // Store the handler reference so it can be properly removed
108
- const dismissHandler = () => {
109
- if (isVisible) {
110
- enhancedComponent.hide()
111
- }
112
- }
113
-
114
- component.on('dismiss', dismissHandler)
115
-
116
- // Add cleanup to lifecycle
117
- const originalDestroy = lifecycle.destroy
118
- lifecycle.destroy = () => {
119
- component.off('dismiss', dismissHandler)
120
- originalDestroy?.call(lifecycle)
121
- }
122
- }
123
-
124
- return enhancedComponent
125
- }
@@ -1,69 +0,0 @@
1
- // src/components/snackbar/features.js
2
-
3
- /**
4
- * Adds action button to snackbar
5
- */
6
- export const withActionButton = (config) => (component) => {
7
- if (!config.action) return component
8
-
9
- const button = document.createElement('button')
10
- button.className = `${config.prefix}-snackbar-action`
11
- button.textContent = config.action
12
-
13
- component.element.appendChild(button)
14
-
15
- return {
16
- ...component,
17
- actionButton: button
18
- }
19
- }
20
-
21
- /**
22
- * Adds auto-dismiss timer functionality
23
- */
24
- export const withDismissTimer = (config) => (component) => {
25
- let timeoutId = null
26
-
27
- const startTimer = () => {
28
- // Clear any existing timer
29
- if (timeoutId) {
30
- clearTimeout(timeoutId)
31
- timeoutId = null
32
- }
33
-
34
- // Only start timer if duration is positive and numeric
35
- if (typeof config.duration === 'number' && config.duration > 0) {
36
- timeoutId = setTimeout(() => {
37
- if (component.element && component.emit) {
38
- component.emit('dismiss')
39
- }
40
- }, config.duration)
41
- }
42
- }
43
-
44
- const stopTimer = () => {
45
- if (timeoutId) {
46
- clearTimeout(timeoutId)
47
- timeoutId = null
48
- }
49
- }
50
-
51
- // Clean up on destroy
52
- const originalDestroy = component.lifecycle?.destroy
53
- if (component.lifecycle) {
54
- component.lifecycle.destroy = () => {
55
- stopTimer()
56
- if (originalDestroy) {
57
- originalDestroy.call(component.lifecycle)
58
- }
59
- }
60
- }
61
-
62
- return {
63
- ...component,
64
- timer: {
65
- start: startTimer,
66
- stop: stopTimer
67
- }
68
- }
69
- }
@@ -1,2 +0,0 @@
1
- // src/components/snackbar/index.js
2
- export { default } from './snackbar.js'
@@ -1,63 +0,0 @@
1
- // src/components/snackbar/position.js
2
-
3
- import { SNACKBAR_POSITIONS } from './constants'
4
-
5
- /**
6
- * Adds position handling to snackbar
7
- * @param {Object} config - Position configuration
8
- * @param {string} config.prefix - Component prefix
9
- * @param {string} config.position - Position variant (start, center, end)
10
- */
11
- export const withPosition = (config) => (component) => {
12
- const position = config.position || SNACKBAR_POSITIONS.CENTER
13
- const positionClass = `${config.prefix}-snackbar--${position}`
14
-
15
- // Add position class
16
- component.element.classList.add(positionClass)
17
-
18
- // Method to update position
19
- const setPosition = (newPosition) => {
20
- // Remove current position class
21
- component.element.classList.remove(positionClass)
22
-
23
- // Add new position class
24
- const newPositionClass = `${config.prefix}-snackbar--${newPosition}`
25
- component.element.classList.add(newPositionClass)
26
-
27
- // Update visible state transform for center position
28
- if (component.element.classList.contains(`${config.prefix}-snackbar--visible`)) {
29
- if (newPosition === SNACKBAR_POSITIONS.CENTER) {
30
- component.element.style.transform = 'translateX(-50%) scale(1)'
31
- } else {
32
- component.element.style.transform = 'scale(1)'
33
- }
34
- }
35
- }
36
-
37
- return {
38
- ...component,
39
- position: {
40
- /**
41
- * Get current position
42
- * @returns {string} Current position
43
- */
44
- getPosition: () => position,
45
-
46
- /**
47
- * Set new position
48
- * @param {string} newPosition - New position to set
49
- * @returns {Object} Component instance
50
- */
51
- setPosition: (newPosition) => {
52
- if (Object.values(SNACKBAR_POSITIONS).includes(newPosition)) {
53
- setPosition(newPosition)
54
- return component
55
- } else {
56
- console.warn(`Invalid position: ${newPosition}. Using default: ${SNACKBAR_POSITIONS.CENTER}`)
57
- setPosition(SNACKBAR_POSITIONS.CENTER)
58
- return component
59
- }
60
- }
61
- }
62
- }
63
- }
@@ -1,74 +0,0 @@
1
- // src/components/snackbar/queue.js
2
-
3
- /**
4
- * Creates a queue manager for snackbars
5
- * Ensures only one snackbar is visible at a time
6
- */
7
- export const createSnackbarQueue = () => {
8
- const queue = []
9
- let isProcessing = false
10
-
11
- const processQueue = () => {
12
- if (queue.length === 0) {
13
- isProcessing = false
14
- return
15
- }
16
-
17
- isProcessing = true
18
- const snackbar = queue[0]
19
-
20
- const handleDismiss = () => {
21
- // Remove from queue
22
- queue.shift()
23
- // Remove listener and cleanup
24
- snackbar.off?.('dismiss', handleDismiss)
25
- // Reset processing state if queue is empty
26
- if (queue.length === 0) {
27
- isProcessing = false
28
- } else {
29
- // Process next after a small delay
30
- setTimeout(processQueue, 200)
31
- }
32
- }
33
-
34
- // Handle both normal dismiss and action button dismissal
35
- snackbar.on?.('dismiss', handleDismiss)
36
- snackbar._show()
37
- }
38
-
39
- return {
40
- /**
41
- * Adds a snackbar to the queue
42
- * @param {Object} snackbar - Snackbar instance
43
- */
44
- add (snackbar) {
45
- if (!snackbar._show) {
46
- throw new Error('Snackbar must implement _show method')
47
- }
48
-
49
- queue.push(snackbar)
50
-
51
- // Only start processing if not already processing
52
- if (!isProcessing) {
53
- processQueue()
54
- }
55
- },
56
-
57
- /**
58
- * Clears all pending snackbars
59
- */
60
- clear () {
61
- // Remove all queued items
62
- queue.length = 0
63
- isProcessing = false
64
- },
65
-
66
- /**
67
- * Gets current queue length
68
- * @returns {number} Number of snackbars in queue
69
- */
70
- getLength () {
71
- return queue.length
72
- }
73
- }
74
- }
@@ -1,70 +0,0 @@
1
- // src/components/snackbar/snackbar.js
2
- import { PREFIX } from '../../core/config'
3
- import { pipe } from '../../core/compose'
4
- import { createBase, withElement } from '../../core/compose/component'
5
- import { withActionButton, withDismissTimer } from './features'
6
- import { withPosition } from './position'
7
- import {
8
- withEvents,
9
- withText,
10
- withVariant,
11
- withLifecycle
12
- } from '../../core/compose/features'
13
- import { withAPI } from './api'
14
- import { createSnackbarQueue } from './queue'
15
- import { SNACKBAR_VARIANTS, SNACKBAR_POSITIONS } from './constants'
16
-
17
- // Create a single queue instance to be shared across all snackbars
18
- const queue = createSnackbarQueue()
19
-
20
- /**
21
- * Creates a new Snackbar component
22
- * @param {Object} config - Snackbar configuration
23
- * @param {string} config.message - Message to display
24
- * @param {string} [config.action] - Action button text
25
- * @param {string} [config.variant='basic'] - Snackbar variant
26
- * @param {string} [config.position='center'] - Display position
27
- * @param {number} [config.duration=4000] - Display duration in ms
28
- * @param {string} [config.class] - Additional CSS classes
29
- */
30
- const createSnackbar = (config = {}) => {
31
- const baseConfig = {
32
- ...config,
33
- componentName: 'snackbar',
34
- prefix: PREFIX,
35
- variant: config.variant || SNACKBAR_VARIANTS.BASIC,
36
- position: config.position || SNACKBAR_POSITIONS.CENTER,
37
- duration: config.duration ?? 4000 // Use nullish coalescing to allow 0
38
- }
39
-
40
- try {
41
- return pipe(
42
- createBase,
43
- withEvents(),
44
- withElement({
45
- tag: 'div',
46
- componentName: 'snackbar',
47
- className: config.class
48
- }),
49
- withVariant(baseConfig),
50
- withPosition(baseConfig),
51
- withText({
52
- ...baseConfig,
53
- text: config.message
54
- }),
55
- withActionButton(baseConfig),
56
- withLifecycle(),
57
- // First apply timer
58
- withDismissTimer(baseConfig),
59
- // Then apply API which needs timer
60
- comp => withAPI({
61
- lifecycle: comp.lifecycle,
62
- queue
63
- })(comp)
64
- )(baseConfig)
65
- } catch (error) {
66
- throw new Error(`Failed to create snackbar: ${error.message}`)
67
- }
68
- }
69
-
70
- export default createSnackbar