@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.
- package/README.md +472 -0
- package/dist/base-line.css +5 -0
- package/dist/base-line.css.map +1 -0
- package/dist/fonts/GoogleSansCode-Bold.ttf +0 -0
- package/dist/fonts/GoogleSansCode-BoldItalic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-ExtraBold.ttf +0 -0
- package/dist/fonts/GoogleSansCode-ExtraBoldItalic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-Italic-VariableFont_wght.ttf +0 -0
- package/dist/fonts/GoogleSansCode-Italic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-Light.ttf +0 -0
- package/dist/fonts/GoogleSansCode-LightItalic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-Medium.ttf +0 -0
- package/dist/fonts/GoogleSansCode-MediumItalic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-Regular.ttf +0 -0
- package/dist/fonts/GoogleSansCode-SemiBold.ttf +0 -0
- package/dist/fonts/GoogleSansCode-SemiBoldItalic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-VariableFont_wght.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Black.ttf +0 -0
- package/dist/fonts/RobotoCondensed-BlackItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Bold.ttf +0 -0
- package/dist/fonts/RobotoCondensed-BoldItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-ExtraBold.ttf +0 -0
- package/dist/fonts/RobotoCondensed-ExtraBoldItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-ExtraLight.ttf +0 -0
- package/dist/fonts/RobotoCondensed-ExtraLightItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Italic-VariableFont_wght.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Italic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Light.ttf +0 -0
- package/dist/fonts/RobotoCondensed-LightItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Medium.ttf +0 -0
- package/dist/fonts/RobotoCondensed-MediumItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Regular.ttf +0 -0
- package/dist/fonts/RobotoCondensed-SemiBold.ttf +0 -0
- package/dist/fonts/RobotoCondensed-SemiBoldItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Thin.ttf +0 -0
- package/dist/fonts/RobotoCondensed-ThinItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-VariableFont_wght.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Black.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-BlackItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Bold.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-BoldItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-ExtraBold.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-ExtraBoldItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-ExtraLight.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-ExtraLightItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Italic-VariableFont_wght.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Italic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Light.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-LightItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Medium.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-MediumItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Regular.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-SemiBold.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-SemiBoldItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-VariableFont_wght.ttf +0 -0
- package/dist/fonts/baseline-icons.woff +0 -0
- package/dist/fonts/baseline-icons.woff2 +0 -0
- package/dist/js/accordion.js +103 -0
- package/dist/js/alert.js +91 -0
- package/dist/js/base.js +146 -0
- package/dist/js/button.js +80 -0
- package/dist/js/carousel.js +427 -0
- package/dist/js/collapse.js +233 -0
- package/dist/js/color-modes.js +70 -0
- package/dist/js/component.js +114 -0
- package/dist/js/dropdown.js +348 -0
- package/dist/js/index.js +108 -0
- package/dist/js/modal.js +440 -0
- package/dist/js/offcanvas.js +356 -0
- package/dist/js/popover.js +241 -0
- package/dist/js/swipe.js +143 -0
- package/dist/js/tab.js +285 -0
- package/dist/js/toast.js +228 -0
- package/dist/js/tooltip.js +716 -0
- package/dist/js/util/backdrop.js +133 -0
- package/dist/js/util/component-functions.js +111 -0
- package/dist/js/util/focustrap.js +101 -0
- package/dist/js/util/scrollbar.js +111 -0
- package/dist/js/util.js +564 -0
- package/package.json +47 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Color mode toggler for Base-Line
|
|
3
|
+
* Based on Baseline's color-modes.js
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
(() => {
|
|
7
|
+
'use strict'
|
|
8
|
+
|
|
9
|
+
const getStoredTheme = () => localStorage.getItem('theme')
|
|
10
|
+
const setStoredTheme = theme => localStorage.setItem('theme', theme)
|
|
11
|
+
|
|
12
|
+
const getPreferredTheme = () => {
|
|
13
|
+
const storedTheme = getStoredTheme()
|
|
14
|
+
if (storedTheme) {
|
|
15
|
+
return storedTheme
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const setTheme = theme => {
|
|
22
|
+
if (theme === 'auto') {
|
|
23
|
+
document.documentElement.setAttribute('data-bl-theme', (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'))
|
|
24
|
+
} else {
|
|
25
|
+
document.documentElement.setAttribute('data-bl-theme', theme)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Also set class for backward compatibility
|
|
29
|
+
if (theme === 'auto') {
|
|
30
|
+
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
31
|
+
if (systemTheme === 'dark') {
|
|
32
|
+
document.documentElement.classList.add('theme-dark')
|
|
33
|
+
document.documentElement.classList.remove('theme-light')
|
|
34
|
+
} else {
|
|
35
|
+
document.documentElement.classList.remove('theme-dark')
|
|
36
|
+
document.documentElement.classList.add('theme-light')
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
if (theme === 'dark') {
|
|
40
|
+
document.documentElement.classList.add('theme-dark')
|
|
41
|
+
document.documentElement.classList.remove('theme-light')
|
|
42
|
+
} else {
|
|
43
|
+
document.documentElement.classList.remove('theme-dark')
|
|
44
|
+
document.documentElement.classList.add('theme-light')
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Set theme immediately, before React renders
|
|
50
|
+
setTheme(getPreferredTheme())
|
|
51
|
+
|
|
52
|
+
// Listen for system theme changes
|
|
53
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
|
54
|
+
const storedTheme = getStoredTheme()
|
|
55
|
+
if (!storedTheme || storedTheme === 'auto') {
|
|
56
|
+
setTheme(getPreferredTheme())
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// Expose functions globally for React to use
|
|
61
|
+
window.__setTheme = (theme) => {
|
|
62
|
+
setStoredTheme(theme)
|
|
63
|
+
setTheme(theme)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
window.__getTheme = () => {
|
|
67
|
+
return getPreferredTheme()
|
|
68
|
+
}
|
|
69
|
+
})()
|
|
70
|
+
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base-Line Component Factory
|
|
3
|
+
* Helper to create components with consistent interface
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { BaseComponent } from './base.js'
|
|
7
|
+
import { getElement, getElements, executeAfterTransition, reflow } from './util.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Component Factory
|
|
11
|
+
*/
|
|
12
|
+
export function createComponent(name, ComponentClass) {
|
|
13
|
+
// Register with Data API
|
|
14
|
+
if (typeof window !== 'undefined' && window.BaseLine && window.BaseLine.dataAPI) {
|
|
15
|
+
window.BaseLine.dataAPI.register(name, ComponentClass)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Auto-initialize on DOM ready
|
|
19
|
+
if (typeof document !== 'undefined') {
|
|
20
|
+
const initComponents = () => {
|
|
21
|
+
const selector = `[data-c-${name}]`
|
|
22
|
+
const elements = document.querySelectorAll(selector)
|
|
23
|
+
|
|
24
|
+
elements.forEach(element => {
|
|
25
|
+
if (!element._baseLineComponent) {
|
|
26
|
+
const instance = new ComponentClass(element)
|
|
27
|
+
element._baseLineComponent = instance
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (document.readyState === 'loading') {
|
|
33
|
+
document.addEventListener('DOMContentLoaded', initComponents)
|
|
34
|
+
} else {
|
|
35
|
+
initComponents()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return ComponentClass
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Event handler helpers
|
|
44
|
+
*/
|
|
45
|
+
export function on(element, event, handler) {
|
|
46
|
+
element.addEventListener(event, handler)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function off(element, event, handler) {
|
|
50
|
+
element.removeEventListener(event, handler)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function one(element, event, handler) {
|
|
54
|
+
const onceHandler = (e) => {
|
|
55
|
+
handler(e)
|
|
56
|
+
off(element, event, onceHandler)
|
|
57
|
+
}
|
|
58
|
+
on(element, event, onceHandler)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Class manipulation helpers
|
|
63
|
+
*/
|
|
64
|
+
export function addClass(element, className) {
|
|
65
|
+
if (element && className) {
|
|
66
|
+
element.classList.add(className)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function removeClass(element, className) {
|
|
71
|
+
if (element && className) {
|
|
72
|
+
element.classList.remove(className)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function toggleClass(element, className) {
|
|
77
|
+
if (element && className) {
|
|
78
|
+
element.classList.toggle(className)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function hasClass(element, className) {
|
|
83
|
+
return element && element.classList.contains(className)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Show/hide helpers
|
|
88
|
+
*/
|
|
89
|
+
export function show(element) {
|
|
90
|
+
if (element) {
|
|
91
|
+
element.style.display = ''
|
|
92
|
+
element.removeAttribute('hidden')
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function hide(element) {
|
|
97
|
+
if (element) {
|
|
98
|
+
element.style.display = 'none'
|
|
99
|
+
element.setAttribute('hidden', '')
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function toggle(element) {
|
|
104
|
+
if (element) {
|
|
105
|
+
if (element.style.display === 'none' || element.hasAttribute('hidden')) {
|
|
106
|
+
show(element)
|
|
107
|
+
} else {
|
|
108
|
+
hide(element)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base-Line Dropdown Component
|
|
3
|
+
* Baseline 2.0 compatible dropdown with Base-Line naming
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { BaseComponent } from './base.js'
|
|
7
|
+
import { getElement, getElements, on, off, isElement, isDisabled, isRTL, SelectorEngine } from './util.js'
|
|
8
|
+
import { addClass, removeClass, hasClass } from './component.js'
|
|
9
|
+
|
|
10
|
+
// Import Popper.js
|
|
11
|
+
import * as Popper from '@popperjs/core'
|
|
12
|
+
|
|
13
|
+
const NAME = 'dropdown'
|
|
14
|
+
const DATA_KEY = `c.${NAME}`
|
|
15
|
+
const EVENT_KEY = `.${DATA_KEY}`
|
|
16
|
+
const DATA_API_KEY = `[data-c-${NAME}]`
|
|
17
|
+
|
|
18
|
+
const EVENT_HIDE = `hide${EVENT_KEY}`
|
|
19
|
+
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
|
|
20
|
+
const EVENT_SHOW = `show${EVENT_KEY}`
|
|
21
|
+
const EVENT_SHOWN = `shown${EVENT_KEY}`
|
|
22
|
+
const EVENT_CLICK = `click${EVENT_KEY}`
|
|
23
|
+
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
|
|
24
|
+
|
|
25
|
+
const CLASS_NAME_SHOW = 'is-show'
|
|
26
|
+
const CLASS_NAME_DROPUP = 'c-dropup'
|
|
27
|
+
const CLASS_NAME_DROPEND = 'c-dropend'
|
|
28
|
+
const CLASS_NAME_DROPSTART = 'c-dropstart'
|
|
29
|
+
const SELECTOR_DATA_TOGGLE = '[data-c-toggle="dropdown"]'
|
|
30
|
+
const SELECTOR_MENU = '.c-dropdown-menu'
|
|
31
|
+
const SELECTOR_DROPDOWN_ITEM = '.c-dropdown-item'
|
|
32
|
+
const SELECTOR_NAVBAR_NAV = '.c-navbar-nav'
|
|
33
|
+
const SELECTOR_NAVBAR = '.c-navbar'
|
|
34
|
+
|
|
35
|
+
// Placement constants (for Popper.js) with RTL support
|
|
36
|
+
const PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'
|
|
37
|
+
const PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'
|
|
38
|
+
const PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'
|
|
39
|
+
const PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'
|
|
40
|
+
const PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'
|
|
41
|
+
const PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'
|
|
42
|
+
|
|
43
|
+
class Dropdown extends BaseComponent {
|
|
44
|
+
constructor(element, config) {
|
|
45
|
+
super(element, config)
|
|
46
|
+
this._popper = null
|
|
47
|
+
this._parent = this._element.parentNode // dropdown wrapper
|
|
48
|
+
// Find menu using multiple strategies (like Baseline)
|
|
49
|
+
this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] ||
|
|
50
|
+
SelectorEngine.prev(this._element, SELECTOR_MENU)[0] ||
|
|
51
|
+
SelectorEngine.findOne(SELECTOR_MENU, this._parent)
|
|
52
|
+
this._isShown = false
|
|
53
|
+
this._inNavbar = this._detectNavbar()
|
|
54
|
+
this._init()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static get NAME() {
|
|
58
|
+
return NAME
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static get Default() {
|
|
62
|
+
return {
|
|
63
|
+
offset: [0, 2],
|
|
64
|
+
boundary: 'clippingParents',
|
|
65
|
+
reference: 'toggle',
|
|
66
|
+
display: 'dynamic',
|
|
67
|
+
popperConfig: null,
|
|
68
|
+
autoClose: true
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
_init() {
|
|
73
|
+
this._bindEvents()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
_bindEvents() {
|
|
77
|
+
const toggle = getElement(SELECTOR_DATA_TOGGLE, this._element) || this._element
|
|
78
|
+
|
|
79
|
+
on(toggle, 'click', (e) => {
|
|
80
|
+
e.preventDefault()
|
|
81
|
+
e.stopPropagation()
|
|
82
|
+
this.toggle()
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
// Close on dropdown item click
|
|
86
|
+
if (this._menu) {
|
|
87
|
+
on(this._menu, 'click', SELECTOR_DROPDOWN_ITEM, (e) => {
|
|
88
|
+
// Only close if autoClose is enabled (default: true)
|
|
89
|
+
if (this._config.autoClose !== false) {
|
|
90
|
+
// Check if the item is disabled
|
|
91
|
+
const item = e.target.closest(SELECTOR_DROPDOWN_ITEM)
|
|
92
|
+
if (item && !item.classList.contains('is-disabled') && !item.hasAttribute('disabled')) {
|
|
93
|
+
this.hide()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Close on outside click
|
|
100
|
+
on(document, 'click', (e) => {
|
|
101
|
+
if (this._isShown && !this._parent.contains(e.target)) {
|
|
102
|
+
this.hide()
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
// Close on escape
|
|
107
|
+
on(document, 'keydown', (e) => {
|
|
108
|
+
if (e.key === 'Escape' && this._isShown) {
|
|
109
|
+
this.hide()
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
_detectNavbar() {
|
|
115
|
+
return this._element.closest(SELECTOR_NAVBAR) !== null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
_getPlacement() {
|
|
119
|
+
const parentDropdown = this._parent
|
|
120
|
+
|
|
121
|
+
if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {
|
|
122
|
+
return PLACEMENT_RIGHT
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {
|
|
126
|
+
return PLACEMENT_LEFT
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// We need to trim the value because custom properties can also include spaces
|
|
130
|
+
const isEnd = this._menu && window.getComputedStyle(this._menu).getPropertyValue('--bl-position').trim() === 'end'
|
|
131
|
+
|
|
132
|
+
if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {
|
|
133
|
+
return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
_getOffset() {
|
|
140
|
+
const { offset } = this._config
|
|
141
|
+
|
|
142
|
+
if (typeof offset === 'string') {
|
|
143
|
+
return offset.split(',').map(value => Number.parseInt(value, 10))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (typeof offset === 'function') {
|
|
147
|
+
return popperData => offset(popperData, this._element)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return offset
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
_getPopperConfig() {
|
|
154
|
+
if (typeof Popper === 'undefined') {
|
|
155
|
+
throw new TypeError('Base-Line\'s dropdowns require Popper (https://popper.js.org/docs/v2/)')
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const defaultBsPopperConfig = {
|
|
159
|
+
placement: this._getPlacement(),
|
|
160
|
+
modifiers: [{
|
|
161
|
+
name: 'preventOverflow',
|
|
162
|
+
options: {
|
|
163
|
+
boundary: this._config.boundary
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: 'offset',
|
|
168
|
+
options: {
|
|
169
|
+
offset: this._getOffset()
|
|
170
|
+
}
|
|
171
|
+
}]
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Disable Popper if we have a static display or Dropdown is in Navbar
|
|
175
|
+
if (this._inNavbar || this._config.display === 'static') {
|
|
176
|
+
if (this._menu) {
|
|
177
|
+
this._menu.setAttribute('data-bl-popper', 'static')
|
|
178
|
+
}
|
|
179
|
+
defaultBsPopperConfig.modifiers = [{
|
|
180
|
+
name: 'applyStyles',
|
|
181
|
+
enabled: false
|
|
182
|
+
}]
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
...defaultBsPopperConfig,
|
|
187
|
+
...(typeof this._config.popperConfig === 'function'
|
|
188
|
+
? this._config.popperConfig(undefined, defaultBsPopperConfig)
|
|
189
|
+
: this._config.popperConfig || {})
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
_createPopper() {
|
|
194
|
+
if (typeof Popper === 'undefined') {
|
|
195
|
+
throw new TypeError('Base-Line\'s dropdowns require Popper (https://popper.js.org/docs/v2/)')
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (!this._menu) {
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
let referenceElement = this._element
|
|
203
|
+
|
|
204
|
+
if (this._config.reference === 'parent') {
|
|
205
|
+
referenceElement = this._parent
|
|
206
|
+
} else if (isElement(this._config.reference)) {
|
|
207
|
+
referenceElement = getElement(this._config.reference)
|
|
208
|
+
} else if (typeof this._config.reference === 'object') {
|
|
209
|
+
referenceElement = this._config.reference
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const popperConfig = this._getPopperConfig()
|
|
213
|
+
this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig)
|
|
214
|
+
|
|
215
|
+
// Set data-bl-popper attribute to enable CSS positioning (always set, even with Popper)
|
|
216
|
+
// This is needed for CSS positioning rules to work
|
|
217
|
+
this._menu.setAttribute('data-bl-popper', '')
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
show() {
|
|
221
|
+
if (this._isShown || isDisabled(this._element)) {
|
|
222
|
+
return
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const showEvent = new CustomEvent(EVENT_SHOW, {
|
|
226
|
+
bubbles: true,
|
|
227
|
+
cancelable: true
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
this._parent.dispatchEvent(showEvent)
|
|
231
|
+
|
|
232
|
+
if (showEvent.defaultPrevented) {
|
|
233
|
+
return
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Close all other open dropdowns before showing this one
|
|
237
|
+
// Find all dropdown menus that are currently shown
|
|
238
|
+
const openMenus = document.querySelectorAll(`${SELECTOR_MENU}.${CLASS_NAME_SHOW}`)
|
|
239
|
+
openMenus.forEach(menu => {
|
|
240
|
+
if (menu !== this._menu) {
|
|
241
|
+
// Find the parent dropdown container
|
|
242
|
+
const parent = menu.closest('.c-dropdown')
|
|
243
|
+
if (parent && parent !== this._parent) {
|
|
244
|
+
// Find the toggle element for this dropdown
|
|
245
|
+
const toggle = SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, parent) ||
|
|
246
|
+
parent.querySelector(SELECTOR_DATA_TOGGLE)
|
|
247
|
+
if (toggle) {
|
|
248
|
+
// Get or create the dropdown instance
|
|
249
|
+
const otherDropdown = Dropdown.getOrCreateInstance(toggle)
|
|
250
|
+
if (otherDropdown && otherDropdown._isShown) {
|
|
251
|
+
otherDropdown.hide()
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
this._createPopper()
|
|
259
|
+
|
|
260
|
+
this._isShown = true
|
|
261
|
+
|
|
262
|
+
if (this._menu) {
|
|
263
|
+
addClass(this._menu, CLASS_NAME_SHOW)
|
|
264
|
+
addClass(this._parent, CLASS_NAME_SHOW)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
this._element.setAttribute('aria-expanded', 'true')
|
|
268
|
+
|
|
269
|
+
const shownEvent = new CustomEvent(EVENT_SHOWN, { bubbles: true })
|
|
270
|
+
this._parent.dispatchEvent(shownEvent)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
hide() {
|
|
274
|
+
if (!this._isShown) {
|
|
275
|
+
return
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const hideEvent = new CustomEvent(EVENT_HIDE, {
|
|
279
|
+
bubbles: true,
|
|
280
|
+
cancelable: true
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
this._parent.dispatchEvent(hideEvent)
|
|
284
|
+
|
|
285
|
+
if (hideEvent.defaultPrevented) {
|
|
286
|
+
return
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
this._isShown = false
|
|
290
|
+
|
|
291
|
+
if (this._popper) {
|
|
292
|
+
this._popper.destroy()
|
|
293
|
+
this._popper = null
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (this._menu) {
|
|
297
|
+
removeClass(this._menu, CLASS_NAME_SHOW)
|
|
298
|
+
removeClass(this._parent, CLASS_NAME_SHOW)
|
|
299
|
+
this._menu.removeAttribute('data-bl-popper')
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
this._element.setAttribute('aria-expanded', 'false')
|
|
303
|
+
|
|
304
|
+
const hiddenEvent = new CustomEvent(EVENT_HIDDEN, { bubbles: true })
|
|
305
|
+
this._parent.dispatchEvent(hiddenEvent)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
toggle() {
|
|
309
|
+
if (this._isShown) {
|
|
310
|
+
this.hide()
|
|
311
|
+
} else {
|
|
312
|
+
this.show()
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
dispose() {
|
|
317
|
+
if (this._popper) {
|
|
318
|
+
this._popper.destroy()
|
|
319
|
+
this._popper = null
|
|
320
|
+
}
|
|
321
|
+
super.dispose()
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
update() {
|
|
325
|
+
this._inNavbar = this._detectNavbar()
|
|
326
|
+
if (this._popper) {
|
|
327
|
+
this._popper.update()
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
static getOrCreateInstance(element, config = {}) {
|
|
332
|
+
return element._baseLineComponent || new Dropdown(element, config)
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Data API
|
|
337
|
+
on(document, 'click', SELECTOR_DATA_TOGGLE, function (event) {
|
|
338
|
+
event.preventDefault()
|
|
339
|
+
event.stopPropagation()
|
|
340
|
+
|
|
341
|
+
// Use the toggle element directly (like Baseline)
|
|
342
|
+
const dropdown = Dropdown.getOrCreateInstance(this)
|
|
343
|
+
dropdown.toggle()
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
export default Dropdown
|
|
347
|
+
|
|
348
|
+
|
package/dist/js/index.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base-Line JavaScript Components
|
|
3
|
+
* Main entry point for all components
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { dataAPI, BaseComponent } from './base.js'
|
|
7
|
+
import { onDOMContentLoaded } from './util.js'
|
|
8
|
+
|
|
9
|
+
// Import all components
|
|
10
|
+
import Alert from './alert.js'
|
|
11
|
+
import Button from './button.js'
|
|
12
|
+
import Carousel from './carousel.js'
|
|
13
|
+
import Collapse from './collapse.js'
|
|
14
|
+
import Dropdown from './dropdown.js'
|
|
15
|
+
import Modal from './modal.js'
|
|
16
|
+
import Offcanvas from './offcanvas.js'
|
|
17
|
+
import Popover from './popover.js'
|
|
18
|
+
import Tab from './tab.js'
|
|
19
|
+
import Toast from './toast.js'
|
|
20
|
+
import Tooltip from './tooltip.js'
|
|
21
|
+
import Accordion from './accordion.js'
|
|
22
|
+
|
|
23
|
+
// Register all components with Data API
|
|
24
|
+
const components = {
|
|
25
|
+
Alert,
|
|
26
|
+
Button,
|
|
27
|
+
Carousel,
|
|
28
|
+
Collapse,
|
|
29
|
+
Dropdown,
|
|
30
|
+
Modal,
|
|
31
|
+
Offcanvas,
|
|
32
|
+
Popover,
|
|
33
|
+
Tab,
|
|
34
|
+
Toast,
|
|
35
|
+
Tooltip,
|
|
36
|
+
Accordion
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Register components
|
|
40
|
+
Object.keys(components).forEach(name => {
|
|
41
|
+
const ComponentClass = components[name]
|
|
42
|
+
if (ComponentClass && ComponentClass.NAME) {
|
|
43
|
+
dataAPI.register(ComponentClass.NAME, ComponentClass)
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// Initialize on DOM ready
|
|
48
|
+
onDOMContentLoaded(() => {
|
|
49
|
+
dataAPI.init()
|
|
50
|
+
|
|
51
|
+
// Trigger load event for components that need it (e.g., Tab)
|
|
52
|
+
const loadEvent = new Event('load.c.tab')
|
|
53
|
+
window.dispatchEvent(loadEvent)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
// Export for global access
|
|
57
|
+
if (typeof window !== 'undefined') {
|
|
58
|
+
window.BaseLine = window.BaseLine || {}
|
|
59
|
+
window.BaseLine.components = components
|
|
60
|
+
window.BaseLine.dataAPI = dataAPI
|
|
61
|
+
window.BaseLine.BaseComponent = BaseComponent
|
|
62
|
+
// Export individual components for direct access
|
|
63
|
+
window.BaseLine.Alert = Alert
|
|
64
|
+
window.BaseLine.Button = Button
|
|
65
|
+
window.BaseLine.Carousel = Carousel
|
|
66
|
+
window.BaseLine.Collapse = Collapse
|
|
67
|
+
window.BaseLine.Dropdown = Dropdown
|
|
68
|
+
window.BaseLine.Modal = Modal
|
|
69
|
+
window.BaseLine.Offcanvas = Offcanvas
|
|
70
|
+
window.BaseLine.Popover = Popover
|
|
71
|
+
window.BaseLine.Tab = Tab
|
|
72
|
+
window.BaseLine.Toast = Toast
|
|
73
|
+
window.BaseLine.Tooltip = Tooltip
|
|
74
|
+
window.BaseLine.Accordion = Accordion
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Export individual components
|
|
78
|
+
export {
|
|
79
|
+
Alert,
|
|
80
|
+
Button,
|
|
81
|
+
Carousel,
|
|
82
|
+
Collapse,
|
|
83
|
+
Dropdown,
|
|
84
|
+
Modal,
|
|
85
|
+
Offcanvas,
|
|
86
|
+
Popover,
|
|
87
|
+
Tab,
|
|
88
|
+
Toast,
|
|
89
|
+
Tooltip,
|
|
90
|
+
Accordion,
|
|
91
|
+
dataAPI,
|
|
92
|
+
BaseComponent
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export default {
|
|
96
|
+
Alert,
|
|
97
|
+
Button,
|
|
98
|
+
Carousel,
|
|
99
|
+
Collapse,
|
|
100
|
+
Dropdown,
|
|
101
|
+
Modal,
|
|
102
|
+
Offcanvas,
|
|
103
|
+
Popover,
|
|
104
|
+
Tab,
|
|
105
|
+
Toast,
|
|
106
|
+
Tooltip,
|
|
107
|
+
Accordion
|
|
108
|
+
}
|