@rhavenside/baseline 2.0.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 (80) hide show
  1. package/README.md +472 -0
  2. package/dist/base-line.css +5 -0
  3. package/dist/base-line.css.map +1 -0
  4. package/dist/fonts/GoogleSansCode-Bold.ttf +0 -0
  5. package/dist/fonts/GoogleSansCode-BoldItalic.ttf +0 -0
  6. package/dist/fonts/GoogleSansCode-ExtraBold.ttf +0 -0
  7. package/dist/fonts/GoogleSansCode-ExtraBoldItalic.ttf +0 -0
  8. package/dist/fonts/GoogleSansCode-Italic-VariableFont_wght.ttf +0 -0
  9. package/dist/fonts/GoogleSansCode-Italic.ttf +0 -0
  10. package/dist/fonts/GoogleSansCode-Light.ttf +0 -0
  11. package/dist/fonts/GoogleSansCode-LightItalic.ttf +0 -0
  12. package/dist/fonts/GoogleSansCode-Medium.ttf +0 -0
  13. package/dist/fonts/GoogleSansCode-MediumItalic.ttf +0 -0
  14. package/dist/fonts/GoogleSansCode-Regular.ttf +0 -0
  15. package/dist/fonts/GoogleSansCode-SemiBold.ttf +0 -0
  16. package/dist/fonts/GoogleSansCode-SemiBoldItalic.ttf +0 -0
  17. package/dist/fonts/GoogleSansCode-VariableFont_wght.ttf +0 -0
  18. package/dist/fonts/RobotoCondensed-Black.ttf +0 -0
  19. package/dist/fonts/RobotoCondensed-BlackItalic.ttf +0 -0
  20. package/dist/fonts/RobotoCondensed-Bold.ttf +0 -0
  21. package/dist/fonts/RobotoCondensed-BoldItalic.ttf +0 -0
  22. package/dist/fonts/RobotoCondensed-ExtraBold.ttf +0 -0
  23. package/dist/fonts/RobotoCondensed-ExtraBoldItalic.ttf +0 -0
  24. package/dist/fonts/RobotoCondensed-ExtraLight.ttf +0 -0
  25. package/dist/fonts/RobotoCondensed-ExtraLightItalic.ttf +0 -0
  26. package/dist/fonts/RobotoCondensed-Italic-VariableFont_wght.ttf +0 -0
  27. package/dist/fonts/RobotoCondensed-Italic.ttf +0 -0
  28. package/dist/fonts/RobotoCondensed-Light.ttf +0 -0
  29. package/dist/fonts/RobotoCondensed-LightItalic.ttf +0 -0
  30. package/dist/fonts/RobotoCondensed-Medium.ttf +0 -0
  31. package/dist/fonts/RobotoCondensed-MediumItalic.ttf +0 -0
  32. package/dist/fonts/RobotoCondensed-Regular.ttf +0 -0
  33. package/dist/fonts/RobotoCondensed-SemiBold.ttf +0 -0
  34. package/dist/fonts/RobotoCondensed-SemiBoldItalic.ttf +0 -0
  35. package/dist/fonts/RobotoCondensed-Thin.ttf +0 -0
  36. package/dist/fonts/RobotoCondensed-ThinItalic.ttf +0 -0
  37. package/dist/fonts/RobotoCondensed-VariableFont_wght.ttf +0 -0
  38. package/dist/fonts/ZalandoSansExpanded-Black.ttf +0 -0
  39. package/dist/fonts/ZalandoSansExpanded-BlackItalic.ttf +0 -0
  40. package/dist/fonts/ZalandoSansExpanded-Bold.ttf +0 -0
  41. package/dist/fonts/ZalandoSansExpanded-BoldItalic.ttf +0 -0
  42. package/dist/fonts/ZalandoSansExpanded-ExtraBold.ttf +0 -0
  43. package/dist/fonts/ZalandoSansExpanded-ExtraBoldItalic.ttf +0 -0
  44. package/dist/fonts/ZalandoSansExpanded-ExtraLight.ttf +0 -0
  45. package/dist/fonts/ZalandoSansExpanded-ExtraLightItalic.ttf +0 -0
  46. package/dist/fonts/ZalandoSansExpanded-Italic-VariableFont_wght.ttf +0 -0
  47. package/dist/fonts/ZalandoSansExpanded-Italic.ttf +0 -0
  48. package/dist/fonts/ZalandoSansExpanded-Light.ttf +0 -0
  49. package/dist/fonts/ZalandoSansExpanded-LightItalic.ttf +0 -0
  50. package/dist/fonts/ZalandoSansExpanded-Medium.ttf +0 -0
  51. package/dist/fonts/ZalandoSansExpanded-MediumItalic.ttf +0 -0
  52. package/dist/fonts/ZalandoSansExpanded-Regular.ttf +0 -0
  53. package/dist/fonts/ZalandoSansExpanded-SemiBold.ttf +0 -0
  54. package/dist/fonts/ZalandoSansExpanded-SemiBoldItalic.ttf +0 -0
  55. package/dist/fonts/ZalandoSansExpanded-VariableFont_wght.ttf +0 -0
  56. package/dist/fonts/baseline-icons.woff +0 -0
  57. package/dist/fonts/baseline-icons.woff2 +0 -0
  58. package/dist/js/accordion.js +103 -0
  59. package/dist/js/alert.js +91 -0
  60. package/dist/js/base.js +146 -0
  61. package/dist/js/button.js +80 -0
  62. package/dist/js/carousel.js +427 -0
  63. package/dist/js/collapse.js +233 -0
  64. package/dist/js/color-modes.js +70 -0
  65. package/dist/js/component.js +114 -0
  66. package/dist/js/dropdown.js +348 -0
  67. package/dist/js/index.js +108 -0
  68. package/dist/js/modal.js +440 -0
  69. package/dist/js/offcanvas.js +356 -0
  70. package/dist/js/popover.js +241 -0
  71. package/dist/js/swipe.js +143 -0
  72. package/dist/js/tab.js +285 -0
  73. package/dist/js/toast.js +228 -0
  74. package/dist/js/tooltip.js +716 -0
  75. package/dist/js/util/backdrop.js +133 -0
  76. package/dist/js/util/component-functions.js +111 -0
  77. package/dist/js/util/focustrap.js +101 -0
  78. package/dist/js/util/scrollbar.js +111 -0
  79. package/dist/js/util.js +564 -0
  80. package/package.json +47 -0
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Base-Line Backdrop Utility
3
+ * Baseline 2.0 compatible backdrop with Base-Line naming
4
+ */
5
+
6
+ import { on, off, executeAfterTransition, reflow, getElement } from '../util.js'
7
+
8
+ /**
9
+ * Constants
10
+ */
11
+
12
+ const CLASS_NAME_FADE = 'c-fade'
13
+ const CLASS_NAME_SHOW = 'is-show'
14
+ const EVENT_MOUSEDOWN = `mousedown.c.backdrop`
15
+
16
+ const Default = {
17
+ className: 'c-modal-backdrop',
18
+ clickCallback: null,
19
+ isAnimated: false,
20
+ isVisible: true,
21
+ rootElement: 'body'
22
+ }
23
+
24
+ /**
25
+ * Class definition
26
+ */
27
+
28
+ class Backdrop {
29
+ constructor(config) {
30
+ this._config = { ...Default, ...config }
31
+ this._isAppended = false
32
+ this._element = null
33
+
34
+ // Convert rootElement string to element
35
+ if (typeof this._config.rootElement === 'string') {
36
+ this._config.rootElement = getElement(this._config.rootElement) || document.body
37
+ }
38
+ }
39
+
40
+ static get Default() {
41
+ return Default
42
+ }
43
+
44
+ static get NAME() {
45
+ return 'backdrop'
46
+ }
47
+
48
+ // Public
49
+ show(callback) {
50
+ if (!this._config.isVisible) {
51
+ if (callback) callback()
52
+ return
53
+ }
54
+
55
+ this._append()
56
+
57
+ const element = this._getElement()
58
+ if (this._config.isAnimated) {
59
+ reflow(element)
60
+ }
61
+
62
+ element.classList.add(CLASS_NAME_SHOW)
63
+
64
+ this._emulateAnimation(() => {
65
+ if (callback) callback()
66
+ })
67
+ }
68
+
69
+ hide(callback) {
70
+ if (!this._config.isVisible) {
71
+ if (callback) callback()
72
+ return
73
+ }
74
+
75
+ this._getElement().classList.remove(CLASS_NAME_SHOW)
76
+
77
+ this._emulateAnimation(() => {
78
+ this.dispose()
79
+ if (callback) callback()
80
+ })
81
+ }
82
+
83
+ dispose() {
84
+ if (!this._isAppended) {
85
+ return
86
+ }
87
+
88
+ off(this._element, EVENT_MOUSEDOWN)
89
+
90
+ this._element.remove()
91
+ this._isAppended = false
92
+ this._element = null
93
+ }
94
+
95
+ // Private
96
+ _getElement() {
97
+ if (!this._element) {
98
+ const backdrop = document.createElement('div')
99
+ backdrop.className = this._config.className
100
+ if (this._config.isAnimated) {
101
+ backdrop.classList.add(CLASS_NAME_FADE)
102
+ }
103
+
104
+ this._element = backdrop
105
+ }
106
+
107
+ return this._element
108
+ }
109
+
110
+ _append() {
111
+ if (this._isAppended) {
112
+ return
113
+ }
114
+
115
+ const element = this._getElement()
116
+ this._config.rootElement.append(element)
117
+
118
+ on(element, EVENT_MOUSEDOWN, () => {
119
+ if (this._config.clickCallback) {
120
+ this._config.clickCallback()
121
+ }
122
+ })
123
+
124
+ this._isAppended = true
125
+ }
126
+
127
+ _emulateAnimation(callback) {
128
+ executeAfterTransition(callback, this._getElement(), this._config.isAnimated)
129
+ }
130
+ }
131
+
132
+ export default Backdrop
133
+
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Base-Line Component Functions Utility
3
+ * Baseline 2.0 compatible component functions with Base-Line naming
4
+ */
5
+
6
+ import { SelectorEngine } from '../util.js'
7
+ import { isDisabled } from '../util.js'
8
+
9
+ /**
10
+ * Enable dismiss trigger for components
11
+ * @param {Function} component - Component class
12
+ * @param {string} method - Method name to call (default: 'hide')
13
+ */
14
+ export const enableDismissTrigger = (component, method = 'hide') => {
15
+ const clickEvent = `click.dismiss${component.EVENT_KEY}`
16
+ const name = component.NAME
17
+ const selector = `[data-c-dismiss="${name}"]`
18
+
19
+ // Use direct event listener with capture phase to catch events before React
20
+ // This matches Bootstrap's approach but uses capture phase for React compatibility
21
+ document.addEventListener('click', function (event) {
22
+ // Traverse up the DOM tree to find the dismiss button (similar to Bootstrap's approach)
23
+ let target = event.target
24
+ let dismissButton = null
25
+
26
+ while (target && target !== document) {
27
+ if (target.matches && target.matches(selector)) {
28
+ dismissButton = target
29
+ break
30
+ }
31
+ target = target.parentElement
32
+ }
33
+
34
+ if (!dismissButton) {
35
+ return
36
+ }
37
+
38
+ if (['A', 'AREA'].includes(dismissButton.tagName)) {
39
+ event.preventDefault()
40
+ }
41
+
42
+ // Stop propagation to prevent React from interfering
43
+ event.stopPropagation()
44
+
45
+ if (isDisabled(dismissButton)) {
46
+ return
47
+ }
48
+
49
+ // First try to find the component element using closest (for dismiss buttons inside the component)
50
+ // This is the most reliable method
51
+ let componentElement = dismissButton.closest(`.c-${name}`)
52
+
53
+ // If closest didn't work, try getElementFromSelector (for buttons with data-c-target pointing to the component)
54
+ if (!componentElement) {
55
+ componentElement = SelectorEngine.getElementFromSelector(dismissButton)
56
+ }
57
+
58
+ // If still not found, search for all components of this type and check if they contain the dismiss button
59
+ // This handles cases where the dismiss button is not a direct child of the component
60
+ if (!componentElement) {
61
+ const allComponents = document.querySelectorAll(`.c-${name}`)
62
+
63
+ for (const comp of allComponents) {
64
+ // Check if component contains the dismiss button
65
+ if (comp.contains(dismissButton)) {
66
+ componentElement = comp
67
+ break
68
+ }
69
+ // Also check if component has a dismiss button that matches
70
+ const dismissBtnInComp = comp.querySelector(`[data-c-dismiss="${name}"]`)
71
+ if (dismissBtnInComp === dismissButton) {
72
+ componentElement = comp
73
+ break
74
+ }
75
+ }
76
+ }
77
+
78
+ // Last resort: find the first visible component of this type that has the 'show' class
79
+ // Only use this if we really can't find the component any other way
80
+ if (!componentElement) {
81
+ const visibleComponents = Array.from(document.querySelectorAll(`.c-${name}`)).filter(comp =>
82
+ comp.classList.contains('show')
83
+ )
84
+
85
+ if (visibleComponents.length === 1) {
86
+ // Only use this if there's exactly one visible component
87
+ componentElement = visibleComponents[0]
88
+ } else if (visibleComponents.length > 1) {
89
+ // If multiple visible, try to find the one that contains the dismiss button
90
+ for (const comp of visibleComponents) {
91
+ if (comp.contains(dismissButton)) {
92
+ componentElement = comp
93
+ break
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+ if (!componentElement) {
100
+ return
101
+ }
102
+
103
+ const instance = component.getOrCreateInstance(componentElement)
104
+
105
+ // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method
106
+ if (instance && typeof instance[method] === 'function') {
107
+ instance[method]()
108
+ }
109
+ }, true) // Use capture phase to catch events before React
110
+ }
111
+
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Base-Line FocusTrap Utility
3
+ * Baseline 2.0 compatible focus trap with Base-Line naming
4
+ */
5
+
6
+ import { on, off } from '../util.js'
7
+ import { SelectorEngine } from '../util.js'
8
+
9
+ /**
10
+ * Constants
11
+ */
12
+
13
+ const DATA_KEY = 'c.focustrap'
14
+ const EVENT_KEY = `.${DATA_KEY}`
15
+ const EVENT_FOCUSIN = `focusin${EVENT_KEY}`
16
+ const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY}`
17
+
18
+ const TAB_KEY = 'Tab'
19
+ const TAB_NAV_FORWARD = 'forward'
20
+ const TAB_NAV_BACKWARD = 'backward'
21
+
22
+ const Default = {
23
+ autofocus: true,
24
+ trapElement: null
25
+ }
26
+
27
+ /**
28
+ * Class definition
29
+ */
30
+
31
+ class FocusTrap {
32
+ constructor(config) {
33
+ this._config = { ...Default, ...config }
34
+ this._isActive = false
35
+ this._lastTabNavDirection = null
36
+ }
37
+
38
+ static get Default() {
39
+ return Default
40
+ }
41
+
42
+ static get NAME() {
43
+ return 'focustrap'
44
+ }
45
+
46
+ // Public
47
+ activate() {
48
+ if (this._isActive) {
49
+ return
50
+ }
51
+
52
+ if (this._config.autofocus) {
53
+ this._config.trapElement.focus()
54
+ }
55
+
56
+ off(document, EVENT_KEY) // guard against infinite focus loop
57
+ on(document, EVENT_FOCUSIN, (event) => this._handleFocusin(event))
58
+ on(document, EVENT_KEYDOWN_TAB, (event) => this._handleKeydown(event))
59
+
60
+ this._isActive = true
61
+ }
62
+
63
+ deactivate() {
64
+ if (!this._isActive) {
65
+ return
66
+ }
67
+
68
+ this._isActive = false
69
+ off(document, EVENT_KEY)
70
+ }
71
+
72
+ // Private
73
+ _handleFocusin(event) {
74
+ const { trapElement } = this._config
75
+
76
+ if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {
77
+ return
78
+ }
79
+
80
+ const elements = SelectorEngine.focusableChildren(trapElement)
81
+
82
+ if (elements.length === 0) {
83
+ trapElement.focus()
84
+ } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {
85
+ elements[elements.length - 1].focus()
86
+ } else {
87
+ elements[0].focus()
88
+ }
89
+ }
90
+
91
+ _handleKeydown(event) {
92
+ if (event.key !== TAB_KEY) {
93
+ return
94
+ }
95
+
96
+ this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD
97
+ }
98
+ }
99
+
100
+ export default FocusTrap
101
+
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Base-Line ScrollBarHelper Utility
3
+ * Baseline 2.0 compatible scrollbar helper with Base-Line naming
4
+ */
5
+
6
+ import { SelectorEngine, isElement } from '../util.js'
7
+
8
+ /**
9
+ * Constants
10
+ */
11
+
12
+ const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'
13
+ const SELECTOR_STICKY_CONTENT = '.sticky-top'
14
+ const PROPERTY_PADDING = 'padding-right'
15
+ const PROPERTY_MARGIN = 'margin-right'
16
+
17
+ /**
18
+ * Class definition
19
+ */
20
+
21
+ class ScrollBarHelper {
22
+ constructor() {
23
+ this._element = document.body
24
+ }
25
+
26
+ // Public
27
+ getWidth() {
28
+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
29
+ const documentWidth = document.documentElement.clientWidth
30
+ return Math.abs(window.innerWidth - documentWidth)
31
+ }
32
+
33
+ hide() {
34
+ const width = this.getWidth()
35
+ this._disableOverFlow()
36
+ // give padding to element to balance the hidden scrollbar width
37
+ this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width)
38
+ // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth
39
+ this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width)
40
+ this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width)
41
+ }
42
+
43
+ reset() {
44
+ this._resetElementAttributes(this._element, 'overflow')
45
+ this._resetElementAttributes(this._element, PROPERTY_PADDING)
46
+ this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING)
47
+ this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN)
48
+ }
49
+
50
+ isOverflowing() {
51
+ return this.getWidth() > 0
52
+ }
53
+
54
+ // Private
55
+ _disableOverFlow() {
56
+ this._saveInitialAttribute(this._element, 'overflow')
57
+ this._element.style.overflow = 'hidden'
58
+ }
59
+
60
+ _setElementAttributes(selector, styleProperty, callback) {
61
+ const scrollbarWidth = this.getWidth()
62
+ const manipulationCallBack = element => {
63
+ if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {
64
+ return
65
+ }
66
+
67
+ this._saveInitialAttribute(element, styleProperty)
68
+ const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty)
69
+ element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue) || 0)}px`)
70
+ }
71
+
72
+ this._applyManipulationCallback(selector, manipulationCallBack)
73
+ }
74
+
75
+ _saveInitialAttribute(element, styleProperty) {
76
+ const actualValue = element.style.getPropertyValue(styleProperty)
77
+ if (actualValue) {
78
+ element.setAttribute(`data-c-${styleProperty}`, actualValue)
79
+ }
80
+ }
81
+
82
+ _resetElementAttributes(selector, styleProperty) {
83
+ const manipulationCallBack = element => {
84
+ const value = element.getAttribute(`data-c-${styleProperty}`)
85
+ // We only want to remove the property if the value is `null`; the value can also be zero
86
+ if (value === null) {
87
+ element.style.removeProperty(styleProperty)
88
+ return
89
+ }
90
+
91
+ element.removeAttribute(`data-c-${styleProperty}`)
92
+ element.style.setProperty(styleProperty, value)
93
+ }
94
+
95
+ this._applyManipulationCallback(selector, manipulationCallBack)
96
+ }
97
+
98
+ _applyManipulationCallback(selector, callBack) {
99
+ if (isElement(selector)) {
100
+ callBack(selector)
101
+ return
102
+ }
103
+
104
+ for (const sel of SelectorEngine.find(selector, this._element)) {
105
+ callBack(sel)
106
+ }
107
+ }
108
+ }
109
+
110
+ export default ScrollBarHelper
111
+