rip-lang 3.13.65 → 3.13.67
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 +1 -1
- package/docs/dist/rip.js +28 -8
- package/docs/dist/rip.min.js +16 -16
- package/docs/dist/rip.min.js.br +0 -0
- package/docs/index.html +4 -0
- package/docs/ui/accordion.rip +113 -0
- package/docs/ui/autocomplete.rip +141 -0
- package/docs/ui/avatar.rip +37 -0
- package/docs/ui/button.rip +23 -0
- package/docs/ui/checkbox-group.rip +65 -0
- package/docs/ui/checkbox.rip +33 -0
- package/docs/ui/combobox.rip +155 -0
- package/docs/ui/context-menu.rip +105 -0
- package/docs/ui/date-picker.rip +214 -0
- package/docs/ui/dialog.rip +107 -0
- package/docs/ui/drawer.rip +79 -0
- package/docs/ui/editable-value.rip +80 -0
- package/docs/ui/field.rip +53 -0
- package/docs/ui/fieldset.rip +22 -0
- package/docs/ui/form.rip +39 -0
- package/docs/ui/grid.rip +901 -0
- package/docs/ui/index.css +1379 -0
- package/docs/ui/index.html +2097 -0
- package/docs/ui/input.rip +36 -0
- package/docs/ui/menu.rip +162 -0
- package/docs/ui/menubar.rip +155 -0
- package/docs/ui/meter.rip +36 -0
- package/docs/ui/multi-select.rip +158 -0
- package/docs/ui/nav-menu.rip +129 -0
- package/docs/ui/number-field.rip +162 -0
- package/docs/ui/otp-field.rip +89 -0
- package/docs/ui/popover.rip +143 -0
- package/docs/ui/preview-card.rip +73 -0
- package/docs/ui/progress.rip +25 -0
- package/docs/ui/radio-group.rip +67 -0
- package/docs/ui/scroll-area.rip +145 -0
- package/docs/ui/select.rip +184 -0
- package/docs/ui/separator.rip +17 -0
- package/docs/ui/slider.rip +165 -0
- package/docs/ui/tabs.rip +124 -0
- package/docs/ui/toast.rip +87 -0
- package/docs/ui/toggle-group.rip +78 -0
- package/docs/ui/toggle.rip +24 -0
- package/docs/ui/toolbar.rip +46 -0
- package/docs/ui/tooltip.rip +115 -0
- package/package.json +2 -1
- package/src/app.rip +1 -1
- package/src/components.js +3 -4
- package/src/lexer.js +1 -1
|
@@ -0,0 +1,2097 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Rip UI</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
10
|
+
<link rel="stylesheet" href="index.css">
|
|
11
|
+
<style>body { opacity: 0; } body.ready { opacity: 1; transition: opacity 200ms ease-in; }</style>
|
|
12
|
+
<script defer src="../dist/rip.min.js" data-src="
|
|
13
|
+
otp-field.rip
|
|
14
|
+
multi-select.rip
|
|
15
|
+
editable-value.rip
|
|
16
|
+
date-picker.rip
|
|
17
|
+
field.rip
|
|
18
|
+
fieldset.rip
|
|
19
|
+
form.rip
|
|
20
|
+
preview-card.rip
|
|
21
|
+
menubar.rip
|
|
22
|
+
nav-menu.rip
|
|
23
|
+
toggle-group.rip
|
|
24
|
+
radio-group.rip
|
|
25
|
+
checkbox-group.rip
|
|
26
|
+
avatar.rip
|
|
27
|
+
context-menu.rip
|
|
28
|
+
toolbar.rip
|
|
29
|
+
drawer.rip
|
|
30
|
+
autocomplete.rip
|
|
31
|
+
scroll-area.rip
|
|
32
|
+
number-field.rip
|
|
33
|
+
slider.rip
|
|
34
|
+
separator.rip
|
|
35
|
+
button.rip
|
|
36
|
+
input.rip
|
|
37
|
+
toggle.rip
|
|
38
|
+
progress.rip
|
|
39
|
+
meter.rip
|
|
40
|
+
checkbox.rip
|
|
41
|
+
toast.rip
|
|
42
|
+
dialog.rip
|
|
43
|
+
select.rip
|
|
44
|
+
combobox.rip
|
|
45
|
+
tooltip.rip
|
|
46
|
+
tabs.rip
|
|
47
|
+
menu.rip
|
|
48
|
+
popover.rip
|
|
49
|
+
accordion.rip
|
|
50
|
+
grid.rip
|
|
51
|
+
" data-mount="WidgetGallery">
|
|
52
|
+
</script>
|
|
53
|
+
</head>
|
|
54
|
+
<body>
|
|
55
|
+
|
|
56
|
+
<script type="text/rip">
|
|
57
|
+
do ->
|
|
58
|
+
orig = HTMLElement::focus
|
|
59
|
+
HTMLElement::focus = (o) -> orig.call this, { preventScroll: true, ...o }
|
|
60
|
+
|
|
61
|
+
export WidgetGallery = component
|
|
62
|
+
|
|
63
|
+
# ---- State ----
|
|
64
|
+
check1 := false
|
|
65
|
+
check2 := true
|
|
66
|
+
switch1 := false
|
|
67
|
+
toasts := []
|
|
68
|
+
showDialog := false
|
|
69
|
+
fruit := null
|
|
70
|
+
role := null
|
|
71
|
+
searchQuery := ''
|
|
72
|
+
allFruits := ['Apple', 'Apricot', 'Avocado', 'Banana', 'Blueberry', 'Cherry', 'Cranberry', 'Date', 'Fig', 'Grape', 'Kiwi', 'Lemon', 'Lime', 'Mango', 'Orange', 'Papaya', 'Peach', 'Pear', 'Plum', 'Raspberry', 'Strawberry']
|
|
73
|
+
filteredFruits := allFruits
|
|
74
|
+
currentTab := 'overview'
|
|
75
|
+
isBold := false
|
|
76
|
+
isItalic := false
|
|
77
|
+
userName := ''
|
|
78
|
+
progressVal := 0.65
|
|
79
|
+
meterVal := 72
|
|
80
|
+
sliderVal := 40
|
|
81
|
+
rangeVal := [20, 70]
|
|
82
|
+
showDrawer := false
|
|
83
|
+
citySearch := ''
|
|
84
|
+
cities := ['Albuquerque', 'Anchorage', 'Arlington', 'Atlanta', 'Aurora', 'Austin', 'Anaheim', 'Asheville', 'Baltimore', 'Baton Rouge', 'Birmingham', 'Boise', 'Boston', 'Buffalo', 'Bakersfield', 'Bridgeport', 'Charlotte', 'Chicago', 'Dallas', 'Denver', 'Detroit', 'El Paso', 'Houston', 'Miami', 'New York', 'Phoenix', 'Portland', 'San Diego', 'San Francisco', 'Seattle']
|
|
85
|
+
quantity := 1
|
|
86
|
+
price := 9.99
|
|
87
|
+
alignment := 'left'
|
|
88
|
+
fontSize := 'md'
|
|
89
|
+
toppings := ['Cheese']
|
|
90
|
+
cbToppings := ['Cheese']
|
|
91
|
+
fieldEmail := ''
|
|
92
|
+
fieldError := ''
|
|
93
|
+
otpCode := ''
|
|
94
|
+
selectedColors := ['Red']
|
|
95
|
+
editName := 'John Doe'
|
|
96
|
+
pickedDate := null
|
|
97
|
+
dateRange := null
|
|
98
|
+
darkMode := false
|
|
99
|
+
sourceCode := null
|
|
100
|
+
sourceName := ''
|
|
101
|
+
sourceLines := 0
|
|
102
|
+
tocActive := null
|
|
103
|
+
tocFilter := ''
|
|
104
|
+
tocGroups := [
|
|
105
|
+
{ label: 'Selection', ids: ['select', 'combobox', 'multi-select', 'autocomplete'] }
|
|
106
|
+
{ label: 'Toggle', ids: ['checkbox', 'toggle', 'toggle-group', 'radio-group', 'checkbox-group'] }
|
|
107
|
+
{ label: 'Input', ids: ['input', 'number-field', 'slider', 'otp-field', 'date-picker', 'editable-value'] }
|
|
108
|
+
{ label: 'Navigation', ids: ['tabs', 'menu', 'context-menu', 'menubar', 'nav-menu', 'toolbar'] }
|
|
109
|
+
{ label: 'Overlay', ids: ['dialog', 'drawer', 'popover', 'tooltip', 'preview-card', 'toast'] }
|
|
110
|
+
{ label: 'Display', ids: ['button', 'separator', 'progress', 'meter', 'avatar', 'scroll-area'] }
|
|
111
|
+
{ label: 'Form', ids: ['field', 'fieldset', 'form'] }
|
|
112
|
+
{ label: 'Data', ids: ['grid', 'accordion'] }
|
|
113
|
+
]
|
|
114
|
+
_nameFor: (id) -> (tocData.find((c) -> c.id is id))?.name or id
|
|
115
|
+
_viewSource: (id) ->
|
|
116
|
+
entry = tocData.find((c) -> c.id is id)
|
|
117
|
+
return unless entry
|
|
118
|
+
sourceName = entry.name
|
|
119
|
+
sourceLines = entry.lines
|
|
120
|
+
resp = fetch! "#{id}.rip"
|
|
121
|
+
sourceCode = resp.text!
|
|
122
|
+
_closeSource: -> sourceCode = null
|
|
123
|
+
_ensureHljs: (cb) ->
|
|
124
|
+
if window.hljs then return cb()
|
|
125
|
+
link = document.createElement('link')
|
|
126
|
+
link.rel = 'stylesheet'
|
|
127
|
+
link.href = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github.min.css'
|
|
128
|
+
document.head.appendChild(link)
|
|
129
|
+
script = document.createElement('script')
|
|
130
|
+
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js'
|
|
131
|
+
script.onload = cb
|
|
132
|
+
document.head.appendChild(script)
|
|
133
|
+
_onFilter: (e) -> tocFilter = e.target.value
|
|
134
|
+
_getVisibleLinks: -> Array.from(document.querySelectorAll('.toc-nav .toc-group a:not([data-hidden])') or [])
|
|
135
|
+
|
|
136
|
+
_onFilterKey: (e) ->
|
|
137
|
+
if e.key is '/'
|
|
138
|
+
e.preventDefault()
|
|
139
|
+
@_navSection('', 'home')
|
|
140
|
+
tocFilter = ''
|
|
141
|
+
e.target.value = ''
|
|
142
|
+
document.addEventListener 'keyup', (=> e.target.focus()), { once: true }
|
|
143
|
+
return
|
|
144
|
+
if e.key is 'Escape'
|
|
145
|
+
tocFilter = ''
|
|
146
|
+
e.target.value = ''
|
|
147
|
+
else if e.key is 'Enter'
|
|
148
|
+
links = @_getVisibleLinks()
|
|
149
|
+
if links.length is 1
|
|
150
|
+
e.preventDefault()
|
|
151
|
+
tocActive = links[0].getAttribute('href')?.slice(1)
|
|
152
|
+
@_fadeTo(document.getElementById(tocActive))
|
|
153
|
+
tocFilter = ''
|
|
154
|
+
e.target.value = ''
|
|
155
|
+
else if e.key is 'Tab' and not e.shiftKey
|
|
156
|
+
links = @_getVisibleLinks()
|
|
157
|
+
if links.length
|
|
158
|
+
e.preventDefault()
|
|
159
|
+
tocActive = links[0].getAttribute('href')?.slice(1)
|
|
160
|
+
links[0].focus()
|
|
161
|
+
|
|
162
|
+
_getTocGrid: ->
|
|
163
|
+
groups = Array.from(document.querySelectorAll('.toc-nav .toc-group') or [])
|
|
164
|
+
grid = []
|
|
165
|
+
for grp in groups
|
|
166
|
+
col = Array.from(grp.querySelectorAll('a:not([data-hidden])') or [])
|
|
167
|
+
grid.push(col) if col.length
|
|
168
|
+
grid
|
|
169
|
+
|
|
170
|
+
_focusTocLink: (link) ->
|
|
171
|
+
tocActive = link.getAttribute('href')?.slice(1)
|
|
172
|
+
link.focus()
|
|
173
|
+
|
|
174
|
+
_onTocKeydown: (e) ->
|
|
175
|
+
grid = @_getTocGrid()
|
|
176
|
+
return unless grid.length
|
|
177
|
+
focused = document.activeElement
|
|
178
|
+
col = -1
|
|
179
|
+
row = -1
|
|
180
|
+
for c, ci in grid
|
|
181
|
+
ri = c.indexOf(focused)
|
|
182
|
+
if ri >= 0
|
|
183
|
+
col = ci
|
|
184
|
+
row = ri
|
|
185
|
+
break
|
|
186
|
+
return if col < 0
|
|
187
|
+
switch e.key
|
|
188
|
+
when 'ArrowDown'
|
|
189
|
+
e.preventDefault()
|
|
190
|
+
if row < grid[col].length - 1
|
|
191
|
+
@_focusTocLink(grid[col][row + 1])
|
|
192
|
+
else if col < grid.length - 1
|
|
193
|
+
@_focusTocLink(grid[col + 1][0])
|
|
194
|
+
when 'ArrowUp'
|
|
195
|
+
e.preventDefault()
|
|
196
|
+
if row > 0
|
|
197
|
+
@_focusTocLink(grid[col][row - 1])
|
|
198
|
+
else if col > 0
|
|
199
|
+
prev = grid[col - 1]
|
|
200
|
+
@_focusTocLink(prev[prev.length - 1])
|
|
201
|
+
else
|
|
202
|
+
@_root?.querySelector('.toc-search')?.focus()
|
|
203
|
+
when 'ArrowRight'
|
|
204
|
+
e.preventDefault()
|
|
205
|
+
if col < grid.length - 1
|
|
206
|
+
target = grid[col + 1]
|
|
207
|
+
@_focusTocLink(target[Math.min(row, target.length - 1)])
|
|
208
|
+
when 'ArrowLeft'
|
|
209
|
+
e.preventDefault()
|
|
210
|
+
if col > 0
|
|
211
|
+
target = grid[col - 1]
|
|
212
|
+
@_focusTocLink(target[Math.min(row, target.length - 1)])
|
|
213
|
+
when 'Home'
|
|
214
|
+
e.preventDefault()
|
|
215
|
+
@_focusTocLink(grid[0][0])
|
|
216
|
+
when 'End'
|
|
217
|
+
e.preventDefault()
|
|
218
|
+
last = grid[grid.length - 1]
|
|
219
|
+
@_focusTocLink(last[last.length - 1])
|
|
220
|
+
when 'Enter', ' '
|
|
221
|
+
e.preventDefault()
|
|
222
|
+
@_fadeTo(document.getElementById(tocActive))
|
|
223
|
+
when 'Escape'
|
|
224
|
+
@_root?.querySelector('.toc-search')?.focus()
|
|
225
|
+
|
|
226
|
+
~>
|
|
227
|
+
q = tocFilter.toLowerCase()
|
|
228
|
+
visible = []
|
|
229
|
+
for el in Array.from(document.querySelectorAll('.toc-nav a') or [])
|
|
230
|
+
if q and not el.textContent.toLowerCase().includes(q)
|
|
231
|
+
el.setAttribute('data-hidden', '')
|
|
232
|
+
else
|
|
233
|
+
el.removeAttribute('data-hidden')
|
|
234
|
+
visible.push(el) if q
|
|
235
|
+
if visible.length is 1
|
|
236
|
+
tocActive = visible[0].getAttribute('href')?.slice(1)
|
|
237
|
+
tocData := [
|
|
238
|
+
{ id: 'select', name: 'Select', lines: 184, desc: 'Dropdown with typeahead, keyboard nav, ARIA listbox.', props: ['@value', '@placeholder', '@disabled'], events: ['change'], keys: ['↕', 'Enter', 'Space', 'Esc', 'Home', 'End', 'type-ahead'], attrs: ['$open', '$placeholder', '$disabled', '$value', '$highlighted', '$selected'] }
|
|
239
|
+
{ id: 'combobox', name: 'Combobox', lines: 153, desc: 'Filterable input + listbox for search-as-you-type.', props: ['@query', '@items', '@placeholder', '@disabled', '@autoHighlight'], events: ['filter', 'select'], keys: ['↕', 'Enter', 'Esc', 'Tab'], attrs: ['$open', '$disabled', '$clear', '$value', '$highlighted', '$empty'] }
|
|
240
|
+
{ id: 'multi-select', name: 'MultiSelect', lines: 158, desc: 'Multi-select with chips, filtering, and keyboard nav.', props: ['@value', '@items', '@placeholder', '@disabled'], events: ['change'], keys: ['↕', 'Enter', 'Esc', 'Backspace', 'Tab'], attrs: ['$open', '$disabled', '$chips', '$chip', '$remove', '$clear', '$highlighted', '$selected'] }
|
|
241
|
+
{ id: 'autocomplete', name: 'Autocomplete', lines: 141, desc: 'Suggestion input — type to filter, select to fill.', props: ['@value', '@items', '@placeholder', '@disabled'], events: ['select'], keys: ['↕', 'Enter', 'Esc', 'Tab'], attrs: ['$open', '$disabled', '$clear'] }
|
|
242
|
+
{ id: 'checkbox', name: 'Checkbox', lines: 33, desc: 'Toggle with checkbox or switch semantics.', props: ['@checked', '@disabled', '@indeterminate', '@switch'], events: ['change'], keys: [], attrs: ['$checked', '$indeterminate', '$disabled'] }
|
|
243
|
+
{ id: 'toggle', name: 'Toggle', lines: 24, desc: 'Two-state toggle button with pressed state.', props: ['@pressed', '@disabled'], events: ['change'], keys: [], attrs: ['$pressed', '$disabled'] }
|
|
244
|
+
{ id: 'toggle-group', name: 'ToggleGroup', lines: 78, desc: 'Single or multi-select toggle buttons.', props: ['@value', '@disabled', '@multiple', '@orientation'], events: ['change'], keys: ['← →', '↕', 'Home', 'End'], attrs: ['$orientation', '$disabled', '$pressed', '$value'] }
|
|
245
|
+
{ id: 'radio-group', name: 'RadioGroup', lines: 67, desc: 'Exactly one option selected with arrow key nav.', props: ['@value', '@disabled', '@orientation', '@name'], events: ['change'], keys: ['← →', '↕', 'Home', 'End'], attrs: ['$orientation', '$disabled', '$checked', '$value'] }
|
|
246
|
+
{ id: 'checkbox-group', name: 'CheckboxGroup', lines: 65, desc: 'Multiple options checked independently.', props: ['@value', '@disabled', '@orientation', '@label'], events: ['change'], keys: ['← →', '↕'], attrs: ['$orientation', '$disabled', '$checked', '$value'] }
|
|
247
|
+
{ id: 'input', name: 'Input', lines: 35, desc: 'Headless input tracking focus, touch, and validation.', props: ['@value', '@placeholder', '@type', '@disabled', '@required'], events: [], keys: [], attrs: ['$disabled', '$focused', '$touched'] }
|
|
248
|
+
{ id: 'number-field', name: 'NumberField', lines: 162, desc: 'Number input with stepper buttons and hold-to-repeat.', props: ['@value', '@min', '@max', '@step', '@disabled', '@readOnly'], events: ['input', 'change'], keys: ['↕', 'PgUp', 'PgDn', 'Home', 'End'], attrs: ['$disabled', '$readonly', '$decrement', '$increment'] }
|
|
249
|
+
{ id: 'slider', name: 'Slider', lines: 165, desc: 'Draggable range input with pointer capture and keyboard.', props: ['@value', '@min', '@max', '@step', '@orientation', '@disabled'], events: ['input', 'change'], keys: ['← →', '↕', 'PgUp', 'PgDn', 'Home', 'End'], attrs: ['$orientation', '$disabled', '$dragging', '$track', '$indicator', '$thumb', '$active'] }
|
|
250
|
+
{ id: 'otp-field', name: 'OTPField', lines: 89, desc: 'Multi-digit code input with auto-advance and paste.', props: ['@length', '@value', '@disabled', '@mask'], events: ['input', 'complete'], keys: ['← →', 'Backspace', 'Home', 'End', 'paste'], attrs: ['$disabled', '$complete', '$filled'] }
|
|
251
|
+
{ id: 'date-picker', name: 'DatePicker', lines: 214, desc: 'Calendar dropdown for single date or range selection.', props: ['@value', '@placeholder', '@disabled', '@range', '@firstDayOfWeek'], events: ['change'], keys: ['Esc', 'Enter', 'Space'], attrs: ['$open', '$disabled', '$range', '$calendar', '$selected', '$today', '$in-range'] }
|
|
252
|
+
{ id: 'editable-value', name: 'EditableValue', lines: 80, desc: 'Click-to-edit inline value with popover form.', props: ['@disabled'], events: ['save'], keys: ['Esc', 'Enter'], attrs: ['$editing', '$disabled', '$saving', '$edit-trigger'] }
|
|
253
|
+
{ id: 'tabs', name: 'Tabs', lines: 124, desc: 'Tab panel with roving tabindex and arrow key nav.', props: ['@active', '@orientation', '@activation'], events: ['change'], keys: ['← →', '↕', 'Home', 'End', 'Enter', 'Space'], attrs: ['$tab', '$panel', '$active', '$disabled'] }
|
|
254
|
+
{ id: 'menu', name: 'Menu', lines: 162, desc: 'Dropdown action menu with keyboard navigation.', props: ['@disabled'], events: ['select'], keys: ['↕', 'Home', 'End', 'Enter', 'Space', 'Esc', 'Tab', 'type-ahead'], attrs: ['$open', '$disabled', '$highlighted', '$value'] }
|
|
255
|
+
{ id: 'context-menu', name: 'ContextMenu', lines: 98, desc: 'Right-click context menu with keyboard navigation.', props: ['@disabled'], events: ['select'], keys: ['↕', 'Home', 'End', 'Enter', 'Space', 'Esc', 'Tab'], attrs: ['$open', '$highlighted', '$disabled', '$value'] }
|
|
256
|
+
{ id: 'menubar', name: 'Menubar', lines: 155, desc: 'Horizontal menu bar with dropdown menus.', props: ['@disabled'], events: ['select'], keys: ['← →', '↕', 'Enter', 'Space', 'Esc', 'Tab'], attrs: ['$disabled', '$open', '$highlighted', '$value'] }
|
|
257
|
+
{ id: 'nav-menu', name: 'NavMenu', lines: 132, desc: 'Site navigation with hover/click dropdown panels.', props: ['@orientation', '@hoverDelay', '@hoverCloseDelay'], events: [], keys: ['← →', '↓', 'Esc'], attrs: ['$orientation', '$open'] }
|
|
258
|
+
{ id: 'toolbar', name: 'Toolbar', lines: 46, desc: 'Groups controls with roving tabindex keyboard nav.', props: ['@orientation', '@label'], events: [], keys: ['← →', '↕', 'Home', 'End'], attrs: ['$orientation'] }
|
|
259
|
+
{ id: 'dialog', name: 'Dialog', lines: 107, desc: 'Modal with focus trap, scroll lock, escape dismiss.', props: ['@open', '@dismissable', '@initialFocus'], events: ['close'], keys: ['Esc', 'Tab'], attrs: ['$open'] }
|
|
260
|
+
{ id: 'drawer', name: 'Drawer', lines: 79, desc: 'Slide-out panel with focus trap and scroll lock.', props: ['@open', '@side', '@dismissable'], events: ['close'], keys: ['Esc', 'Tab'], attrs: ['$open', '$side'] }
|
|
261
|
+
{ id: 'popover', name: 'Popover', lines: 143, desc: 'Anchored floating content with flip/shift positioning.', props: ['@placement', '@offset', '@disabled', '@openOnHover', '@hoverDelay', '@hoverCloseDelay'], events: [], keys: ['Esc', 'Enter', 'Space', '↓'], attrs: ['$open', '$placement'] }
|
|
262
|
+
{ id: 'tooltip', name: 'Tooltip', lines: 115, desc: 'Hover/focus tooltip with delay and positioning.', props: ['@text', '@placement', '@delay', '@offset', '@hoverable'], events: [], keys: [], attrs: ['$open', '$entering', '$exiting', '$placement'] }
|
|
263
|
+
{ id: 'preview-card', name: 'PreviewCard', lines: 73, desc: 'Hover/focus preview card with delay.', props: ['@delay', '@closeDelay'], events: [], keys: [], attrs: ['$open'] }
|
|
264
|
+
{ id: 'toast', name: 'Toast', lines: 88, desc: 'Auto-dismiss notification with ARIA live region.', props: ['@toasts', '@toast', '@placement'], events: ['dismiss'], keys: [], attrs: ['$placement', '$type', '$leaving'] }
|
|
265
|
+
{ id: 'button', name: 'Button', lines: 23, desc: 'Accessible button with disabled-but-focusable pattern.', props: ['@disabled'], events: ['press'], keys: [], attrs: ['$disabled'] }
|
|
266
|
+
{ id: 'separator', name: 'Separator', lines: 17, desc: 'Decorative or semantic divider between sections.', props: ['@orientation', '@decorative'], events: [], keys: [], attrs: ['$orientation'] }
|
|
267
|
+
{ id: 'progress', name: 'Progress', lines: 25, desc: 'Progress bar with CSS custom property for value.', props: ['@value', '@max', '@label'], events: [], keys: [], attrs: ['$complete'] }
|
|
268
|
+
{ id: 'meter', name: 'Meter', lines: 36, desc: 'Gauge for known-range measurements with thresholds.', props: ['@value', '@min', '@max', '@low', '@high', '@optimum', '@label'], events: [], keys: [], attrs: ['$level'] }
|
|
269
|
+
{ id: 'avatar', name: 'Avatar', lines: 37, desc: 'Image with fallback to initials or placeholder.', props: ['@src', '@alt', '@fallback'], events: [], keys: [], attrs: ['$status', '$initials', '$placeholder'] }
|
|
270
|
+
{ id: 'scroll-area', name: 'ScrollArea', lines: 145, desc: 'Custom scrollbar with draggable thumb and auto-hide.', props: ['@orientation'], events: [], keys: [], attrs: ['$orientation', '$hovering', '$scrolling', '$dragging', '$viewport', '$scrollbar', '$thumb'] }
|
|
271
|
+
{ id: 'field', name: 'Field', lines: 53, desc: 'Form field wrapper with label, description, and error.', props: ['@label', '@description', '@error', '@disabled', '@required'], events: [], keys: [], attrs: ['$disabled', '$invalid', '$label', '$required', '$description', '$error'] }
|
|
272
|
+
{ id: 'fieldset', name: 'Fieldset', lines: 22, desc: 'Grouped fields with legend and cascading disable.', props: ['@legend', '@disabled'], events: [], keys: [], attrs: ['$disabled', '$legend'] }
|
|
273
|
+
{ id: 'form', name: 'Form', lines: 39, desc: 'Form wrapper with submit handling and validation state.', props: ['@disabled'], events: ['submit'], keys: [], attrs: ['$disabled', '$submitting', '$submitted'] }
|
|
274
|
+
{ id: 'grid', name: 'Grid', lines: 901, desc: 'Virtual-scrolling data grid — 100K+ rows at 60fps.', props: ['@data', '@columns', '@rowHeight', '@overscan', '@striped', '@beforeEdit', '@afterEdit'], events: [], keys: ['Arrows', 'Tab', 'Enter', 'F2', 'Esc', 'Home', 'End', 'PgUp', 'PgDn', 'Ctrl+A', 'Ctrl+C', 'Ctrl+V', 'Ctrl+X'], attrs: ['$editing', '$selecting', '$sorted'] }
|
|
275
|
+
{ id: 'accordion', name: 'Accordion', lines: 113, desc: 'Expand/collapse sections, single or multiple mode.', props: ['@multiple'], events: ['change'], keys: ['Enter', 'Space', '↕', 'Home', 'End'], attrs: ['$item', '$trigger', '$content', '$open', '$disabled'] }
|
|
276
|
+
]
|
|
277
|
+
_infoName := ''
|
|
278
|
+
_infoDesc := ''
|
|
279
|
+
_infoLines := 0
|
|
280
|
+
_infoProps := []
|
|
281
|
+
_infoEvents := []
|
|
282
|
+
_infoKeys := []
|
|
283
|
+
_infoAttrs := []
|
|
284
|
+
|
|
285
|
+
~>
|
|
286
|
+
info = tocData.find (c) -> c.id is tocActive
|
|
287
|
+
if info
|
|
288
|
+
_infoName = info.name
|
|
289
|
+
_infoDesc = info.desc
|
|
290
|
+
_infoLines = info.lines
|
|
291
|
+
_infoProps = info.props
|
|
292
|
+
_infoEvents = info.events
|
|
293
|
+
_infoKeys = info.keys
|
|
294
|
+
_infoAttrs = info.attrs
|
|
295
|
+
|
|
296
|
+
~>
|
|
297
|
+
raw = sourceCode
|
|
298
|
+
return unless raw
|
|
299
|
+
setTimeout =>
|
|
300
|
+
codeEl = document.querySelector('.source-code')
|
|
301
|
+
gutterEl = document.querySelector('.source-gutter')
|
|
302
|
+
return unless codeEl
|
|
303
|
+
text = raw.replace(/^\n+/, '').replace(/\n+$/, '')
|
|
304
|
+
lines = text.split('\n')
|
|
305
|
+
gutterEl.textContent = lines.map((_, i) -> String(i + 1)).join('\n') if gutterEl
|
|
306
|
+
codeEl.textContent = text
|
|
307
|
+
@_ensureHljs =>
|
|
308
|
+
hljs.highlightElement(codeEl) if window.hljs
|
|
309
|
+
, 0
|
|
310
|
+
|
|
311
|
+
~>
|
|
312
|
+
if sourceCode
|
|
313
|
+
onKey = (e) => @_closeSource() if e.key is 'Escape'
|
|
314
|
+
document.addEventListener 'keydown', onKey
|
|
315
|
+
return -> document.removeEventListener 'keydown', onKey
|
|
316
|
+
|
|
317
|
+
_toggleDark: ->
|
|
318
|
+
darkMode = not darkMode
|
|
319
|
+
document.documentElement.classList.add('no-transition')
|
|
320
|
+
document.documentElement.dataset.theme = if darkMode then 'dark' else ''
|
|
321
|
+
localStorage.setItem('rip-ui-theme', if darkMode then 'dark' else 'light')
|
|
322
|
+
requestAnimationFrame -> requestAnimationFrame -> document.documentElement.classList.remove('no-transition')
|
|
323
|
+
|
|
324
|
+
mounted: ->
|
|
325
|
+
if localStorage.getItem('rip-ui-theme') is 'dark'
|
|
326
|
+
darkMode = true
|
|
327
|
+
document.documentElement.dataset.theme = 'dark'
|
|
328
|
+
document.addEventListener 'keydown', (e) =>
|
|
329
|
+
if e.key is '/' and not (e.target.tagName in ['INPUT', 'TEXTAREA', 'SELECT']) and not e.target.isContentEditable
|
|
330
|
+
e.preventDefault()
|
|
331
|
+
@_navSection('', 'home')
|
|
332
|
+
tocFilter = ''
|
|
333
|
+
document.addEventListener 'keyup', (=>
|
|
334
|
+
@_root?.querySelector('.toc-search')?.focus()
|
|
335
|
+
), { once: true }
|
|
336
|
+
gc = document.querySelector('.grid-container')
|
|
337
|
+
if gc
|
|
338
|
+
gc.addEventListener 'mousedown', => setTimeout (=> @_updateGridRef()), 0
|
|
339
|
+
gc.addEventListener 'keyup', => setTimeout (=> @_updateGridRef()), 0
|
|
340
|
+
hash = location.hash.slice(1)
|
|
341
|
+
if hash and document.getElementById(hash)
|
|
342
|
+
tocActive = hash
|
|
343
|
+
setTimeout =>
|
|
344
|
+
document.getElementById(hash)?.scrollIntoView({ behavior: 'instant', block: 'start' })
|
|
345
|
+
document.body.classList.add('ready')
|
|
346
|
+
, 50
|
|
347
|
+
else
|
|
348
|
+
document.body.classList.add('ready')
|
|
349
|
+
|
|
350
|
+
gridCellRef := ''
|
|
351
|
+
|
|
352
|
+
_updateGridRef: ->
|
|
353
|
+
cell = document.querySelector('.grid-container [data-active]')
|
|
354
|
+
if cell
|
|
355
|
+
row = cell.parentElement
|
|
356
|
+
rows = Array.from(row.parentElement.children).filter (r) -> r.tagName is 'TR' and not r.classList.contains('spacer')
|
|
357
|
+
ri = rows.indexOf(row)
|
|
358
|
+
ci = Array.from(row.children).indexOf(cell)
|
|
359
|
+
gridCellRef = "R#{ri + 1}:C#{ci + 1}" if ri >= 0 and ci >= 0
|
|
360
|
+
else
|
|
361
|
+
gridCellRef = ''
|
|
362
|
+
|
|
363
|
+
_loadGridRows: (n) ->
|
|
364
|
+
firstNames = ['Alice', 'Bob', 'Carol', 'Dan', 'Eve', 'Frank', 'Grace', 'Hank', 'Iris', 'Jack', 'Karen', 'Leo', 'Mia', 'Nora', 'Omar', 'Pia', 'Quinn', 'Rosa', 'Sam', 'Tina']
|
|
365
|
+
lastNames = ['Chen', 'Park', 'Singh', 'Nakamura', 'Torres', 'Liu', 'Kim', 'Patel', 'Wang', 'Brown', 'Lee', 'Martin', 'Garcia', 'Davis', 'Lopez', 'Clark', 'Hall', 'Young', 'King', 'Wright']
|
|
366
|
+
roles = ['Engineer', 'Designer', 'Manager', 'Director', 'Analyst', 'Lead', 'Intern']
|
|
367
|
+
cityList = ['Seattle', 'Portland', 'Denver', 'Austin', 'Chicago', 'Boston', 'Miami', 'Phoenix', 'Dallas', 'Atlanta']
|
|
368
|
+
gc = document.querySelector('.grid-container')
|
|
369
|
+
gc?.scrollTop = 0
|
|
370
|
+
gridData = Array.from { length: n }, (_, idx) ->
|
|
371
|
+
fn = firstNames[idx %% firstNames.length]
|
|
372
|
+
ln = lastNames[Math.floor(Math.random() * lastNames.length)]
|
|
373
|
+
{ _row: idx + 1, name: "#{fn} #{ln}", role: roles[idx %% roles.length], age: 22 + (idx %% 40), city: cityList[idx %% cityList.length] }
|
|
374
|
+
gridCellRef = ''
|
|
375
|
+
|
|
376
|
+
gridColumns := [
|
|
377
|
+
{ key: '_row', title: '#', width: 60, align: 'right' },
|
|
378
|
+
{ key: 'name', title: 'Name', width: 180 },
|
|
379
|
+
{ key: 'role', title: 'Role', width: 140 },
|
|
380
|
+
{ key: 'age', title: 'Age', width: 80, align: 'right' },
|
|
381
|
+
{ key: 'city', title: 'City', width: 140 },
|
|
382
|
+
]
|
|
383
|
+
gridData := [
|
|
384
|
+
{ _row: 1, name: 'Alice Chen', role: 'Engineer', age: 28, city: 'Seattle' },
|
|
385
|
+
{ _row: 2, name: 'Bob Park', role: 'Designer', age: 34, city: 'Portland' },
|
|
386
|
+
{ _row: 3, name: 'Carol Singh', role: 'Manager', age: 41, city: 'Denver' },
|
|
387
|
+
{ _row: 4, name: 'Dan Nakamura', role: 'Engineer', age: 26, city: 'Austin' },
|
|
388
|
+
{ _row: 5, name: 'Eve Torres', role: 'Director', age: 45, city: 'Chicago' },
|
|
389
|
+
{ _row: 6, name: 'Frank Liu', role: 'Designer', age: 31, city: 'Boston' },
|
|
390
|
+
{ _row: 7, name: 'Grace Kim', role: 'Engineer', age: 29, city: 'Seattle' },
|
|
391
|
+
{ _row: 8, name: 'Hank Patel', role: 'Manager', age: 38, city: 'Denver' },
|
|
392
|
+
{ _row: 9, name: 'Iris Wang', role: 'Engineer', age: 33, city: 'Austin' },
|
|
393
|
+
{ _row: 10, name: 'Jack Brown', role: 'Designer', age: 27, city: 'Portland' },
|
|
394
|
+
{ _row: 11, name: 'Karen Lee', role: 'Director', age: 52, city: 'Chicago' },
|
|
395
|
+
{ _row: 12, name: 'Leo Martin', role: 'Engineer', age: 24, city: 'Boston' },
|
|
396
|
+
]
|
|
397
|
+
|
|
398
|
+
sectionIds =! ['select', 'combobox', 'multi-select', 'autocomplete', 'checkbox', 'toggle', 'toggle-group', 'radio-group', 'checkbox-group', 'input', 'number-field', 'slider', 'otp-field', 'date-picker', 'editable-value', 'tabs', 'menu', 'context-menu', 'menubar', 'nav-menu', 'toolbar', 'dialog', 'drawer', 'popover', 'tooltip', 'preview-card', 'toast', 'button', 'separator', 'progress', 'meter', 'avatar', 'scroll-area', 'field', 'form', 'grid', 'accordion']
|
|
399
|
+
|
|
400
|
+
_fadeTo: (el) ->
|
|
401
|
+
return unless el
|
|
402
|
+
gallery = document.querySelector('.gallery')
|
|
403
|
+
gallery.style.transition = 'opacity 80ms ease-out'
|
|
404
|
+
gallery.style.opacity = '0'
|
|
405
|
+
setTimeout =>
|
|
406
|
+
el.scrollIntoView({ behavior: 'instant', block: 'start' })
|
|
407
|
+
history.replaceState(null, '', '#' + el.id) if el.id
|
|
408
|
+
gallery.style.transition = 'opacity 120ms ease-in'
|
|
409
|
+
gallery.style.opacity = '1'
|
|
410
|
+
, 80
|
|
411
|
+
|
|
412
|
+
_jumpTo: (e) ->
|
|
413
|
+
e.preventDefault()
|
|
414
|
+
@_fadeTo(document.getElementById(tocActive))
|
|
415
|
+
|
|
416
|
+
_navSection: (currentId, dir) ->
|
|
417
|
+
if dir is 'home'
|
|
418
|
+
gallery = document.querySelector('.gallery')
|
|
419
|
+
gallery.style.transition = 'opacity 80ms ease-out'
|
|
420
|
+
gallery.style.opacity = '0'
|
|
421
|
+
setTimeout =>
|
|
422
|
+
window.scrollTo({ top: 0, behavior: 'instant' })
|
|
423
|
+
history.replaceState(null, '', location.pathname)
|
|
424
|
+
gallery.style.transition = 'opacity 120ms ease-in'
|
|
425
|
+
gallery.style.opacity = '1'
|
|
426
|
+
, 80
|
|
427
|
+
return
|
|
428
|
+
idx = sectionIds.indexOf(currentId)
|
|
429
|
+
return if idx < 0
|
|
430
|
+
nextId = sectionIds[if dir is 'prev' then idx - 1 else idx + 1]
|
|
431
|
+
@_fadeTo(document.getElementById(nextId)) if nextId
|
|
432
|
+
|
|
433
|
+
render
|
|
434
|
+
.gallery
|
|
435
|
+
|
|
436
|
+
.gallery-header
|
|
437
|
+
.
|
|
438
|
+
h1 "Rip UI"
|
|
439
|
+
button.theme-toggle @click: (=> @_toggleDark())
|
|
440
|
+
if darkMode then "☀" else "☾"
|
|
441
|
+
p "38 headless, accessible components. Zero CSS, zero dependencies."
|
|
442
|
+
|
|
443
|
+
.toc
|
|
444
|
+
.toc-nav @keydown: @_onTocKeydown
|
|
445
|
+
input.toc-search type: "text", placeholder: "Filter components...", @input: @_onFilter, @keydown: @_onFilterKey
|
|
446
|
+
.toc-group
|
|
447
|
+
.toc-label "Selection"
|
|
448
|
+
a @mouseenter: (=> tocActive = 'select'), @click: (=> tocActive = 'select'), href: "#select", $active: (tocActive is 'select')?!, "Select"
|
|
449
|
+
a @mouseenter: (=> tocActive = 'combobox'), @click: (=> tocActive = 'combobox'), href: "#combobox", $active: (tocActive is 'combobox')?!, "Combobox"
|
|
450
|
+
a @mouseenter: (=> tocActive = 'multi-select'), @click: (=> tocActive = 'multi-select'), href: "#multi-select", $active: (tocActive is 'multi-select')?!, "MultiSelect"
|
|
451
|
+
a @mouseenter: (=> tocActive = 'autocomplete'), @click: (=> tocActive = 'autocomplete'), href: "#autocomplete", $active: (tocActive is 'autocomplete')?!, "Autocomplete"
|
|
452
|
+
.toc-group
|
|
453
|
+
.toc-label "Toggle"
|
|
454
|
+
a @mouseenter: (=> tocActive = 'checkbox'), @click: (=> tocActive = 'checkbox'), href: "#checkbox", $active: (tocActive is 'checkbox')?!, "Checkbox"
|
|
455
|
+
a @mouseenter: (=> tocActive = 'toggle'), @click: (=> tocActive = 'toggle'), href: "#toggle", $active: (tocActive is 'toggle')?!, "Toggle"
|
|
456
|
+
a @mouseenter: (=> tocActive = 'toggle-group'), @click: (=> tocActive = 'toggle-group'), href: "#toggle-group", $active: (tocActive is 'toggle-group')?!, "ToggleGroup"
|
|
457
|
+
a @mouseenter: (=> tocActive = 'radio-group'), @click: (=> tocActive = 'radio-group'), href: "#radio-group", $active: (tocActive is 'radio-group')?!, "RadioGroup"
|
|
458
|
+
a @mouseenter: (=> tocActive = 'checkbox-group'), @click: (=> tocActive = 'checkbox-group'), href: "#checkbox-group", $active: (tocActive is 'checkbox-group')?!, "CheckboxGroup"
|
|
459
|
+
.toc-group
|
|
460
|
+
.toc-label "Input"
|
|
461
|
+
a @mouseenter: (=> tocActive = 'input'), @click: (=> tocActive = 'input'), href: "#input", $active: (tocActive is 'input')?!, "Input"
|
|
462
|
+
a @mouseenter: (=> tocActive = 'number-field'), @click: (=> tocActive = 'number-field'), href: "#number-field", $active: (tocActive is 'number-field')?!, "NumberField"
|
|
463
|
+
a @mouseenter: (=> tocActive = 'slider'), @click: (=> tocActive = 'slider'), href: "#slider", $active: (tocActive is 'slider')?!, "Slider"
|
|
464
|
+
a @mouseenter: (=> tocActive = 'otp-field'), @click: (=> tocActive = 'otp-field'), href: "#otp-field", $active: (tocActive is 'otp-field')?!, "OTPField"
|
|
465
|
+
a @mouseenter: (=> tocActive = 'date-picker'), @click: (=> tocActive = 'date-picker'), href: "#date-picker", $active: (tocActive is 'date-picker')?!, "DatePicker"
|
|
466
|
+
a @mouseenter: (=> tocActive = 'editable-value'), @click: (=> tocActive = 'editable-value'), href: "#editable-value", $active: (tocActive is 'editable-value')?!, "EditableValue"
|
|
467
|
+
.toc-group
|
|
468
|
+
.toc-label "Navigation"
|
|
469
|
+
a @mouseenter: (=> tocActive = 'tabs'), @click: (=> tocActive = 'tabs'), href: "#tabs", $active: (tocActive is 'tabs')?!, "Tabs"
|
|
470
|
+
a @mouseenter: (=> tocActive = 'menu'), @click: (=> tocActive = 'menu'), href: "#menu", $active: (tocActive is 'menu')?!, "Menu"
|
|
471
|
+
a @mouseenter: (=> tocActive = 'context-menu'), @click: (=> tocActive = 'context-menu'), href: "#context-menu", $active: (tocActive is 'context-menu')?!, "ContextMenu"
|
|
472
|
+
a @mouseenter: (=> tocActive = 'menubar'), @click: (=> tocActive = 'menubar'), href: "#menubar", $active: (tocActive is 'menubar')?!, "Menubar"
|
|
473
|
+
a @mouseenter: (=> tocActive = 'nav-menu'), @click: (=> tocActive = 'nav-menu'), href: "#nav-menu", $active: (tocActive is 'nav-menu')?!, "NavMenu"
|
|
474
|
+
a @mouseenter: (=> tocActive = 'toolbar'), @click: (=> tocActive = 'toolbar'), href: "#toolbar", $active: (tocActive is 'toolbar')?!, "Toolbar"
|
|
475
|
+
.toc-group
|
|
476
|
+
.toc-label "Overlay"
|
|
477
|
+
a @mouseenter: (=> tocActive = 'dialog'), @click: (=> tocActive = 'dialog'), href: "#dialog", $active: (tocActive is 'dialog')?!, "Dialog"
|
|
478
|
+
a @mouseenter: (=> tocActive = 'drawer'), @click: (=> tocActive = 'drawer'), href: "#drawer", $active: (tocActive is 'drawer')?!, "Drawer"
|
|
479
|
+
a @mouseenter: (=> tocActive = 'popover'), @click: (=> tocActive = 'popover'), href: "#popover", $active: (tocActive is 'popover')?!, "Popover"
|
|
480
|
+
a @mouseenter: (=> tocActive = 'tooltip'), @click: (=> tocActive = 'tooltip'), href: "#tooltip", $active: (tocActive is 'tooltip')?!, "Tooltip"
|
|
481
|
+
a @mouseenter: (=> tocActive = 'preview-card'), @click: (=> tocActive = 'preview-card'), href: "#preview-card", $active: (tocActive is 'preview-card')?!, "PreviewCard"
|
|
482
|
+
a @mouseenter: (=> tocActive = 'toast'), @click: (=> tocActive = 'toast'), href: "#toast", $active: (tocActive is 'toast')?!, "Toast"
|
|
483
|
+
.toc-group
|
|
484
|
+
.toc-label "Display"
|
|
485
|
+
a @mouseenter: (=> tocActive = 'button'), @click: (=> tocActive = 'button'), href: "#button", $active: (tocActive is 'button')?!, "Button"
|
|
486
|
+
a @mouseenter: (=> tocActive = 'separator'), @click: (=> tocActive = 'separator'), href: "#separator", $active: (tocActive is 'separator')?!, "Separator"
|
|
487
|
+
a @mouseenter: (=> tocActive = 'progress'), @click: (=> tocActive = 'progress'), href: "#progress", $active: (tocActive is 'progress')?!, "Progress"
|
|
488
|
+
a @mouseenter: (=> tocActive = 'meter'), @click: (=> tocActive = 'meter'), href: "#meter", $active: (tocActive is 'meter')?!, "Meter"
|
|
489
|
+
a @mouseenter: (=> tocActive = 'avatar'), @click: (=> tocActive = 'avatar'), href: "#avatar", $active: (tocActive is 'avatar')?!, "Avatar"
|
|
490
|
+
a @mouseenter: (=> tocActive = 'scroll-area'), @click: (=> tocActive = 'scroll-area'), href: "#scroll-area", $active: (tocActive is 'scroll-area')?!, "ScrollArea"
|
|
491
|
+
.toc-group
|
|
492
|
+
.toc-label "Form"
|
|
493
|
+
a @mouseenter: (=> tocActive = 'field'), @click: (=> tocActive = 'field'), href: "#field", $active: (tocActive is 'field')?!, "Field"
|
|
494
|
+
a @mouseenter: (=> tocActive = 'fieldset'), @click: (=> tocActive = 'fieldset'), href: "#fieldset", $active: (tocActive is 'fieldset')?!, "Fieldset"
|
|
495
|
+
a @mouseenter: (=> tocActive = 'form'), @click: (=> tocActive = 'form'), href: "#form", $active: (tocActive is 'form')?!, "Form"
|
|
496
|
+
.toc-group
|
|
497
|
+
.toc-label "Data"
|
|
498
|
+
a @mouseenter: (=> tocActive = 'grid'), @click: (=> tocActive = 'grid'), href: "#grid", $active: (tocActive is 'grid')?!, "Grid"
|
|
499
|
+
a @mouseenter: (=> tocActive = 'accordion'), @click: (=> tocActive = 'accordion'), href: "#accordion", $active: (tocActive is 'accordion')?!, "Accordion"
|
|
500
|
+
.toc-detail
|
|
501
|
+
if tocActive
|
|
502
|
+
.
|
|
503
|
+
h3
|
|
504
|
+
_infoName
|
|
505
|
+
span.badge @click: (=> @_viewSource(tocActive)), "#{_infoLines} lines ❐"
|
|
506
|
+
p.toc-desc _infoDesc
|
|
507
|
+
.api
|
|
508
|
+
dl
|
|
509
|
+
dt "Props"
|
|
510
|
+
dd
|
|
511
|
+
for item in _infoProps
|
|
512
|
+
code item
|
|
513
|
+
if _infoEvents.length
|
|
514
|
+
dt "Events"
|
|
515
|
+
dd
|
|
516
|
+
for item in _infoEvents
|
|
517
|
+
code item
|
|
518
|
+
if _infoKeys.length
|
|
519
|
+
dt "Keyboard"
|
|
520
|
+
dd
|
|
521
|
+
for item in _infoKeys
|
|
522
|
+
kbd item
|
|
523
|
+
dt "Data"
|
|
524
|
+
dd
|
|
525
|
+
for item in _infoAttrs
|
|
526
|
+
code item
|
|
527
|
+
a.jump href: "##{tocActive}", @click: ((e) => @_jumpTo(e))
|
|
528
|
+
"Jump to demo ↓"
|
|
529
|
+
else
|
|
530
|
+
.toc-empty "← Hover a component to see its API"
|
|
531
|
+
|
|
532
|
+
# ================================================================
|
|
533
|
+
# SELECT
|
|
534
|
+
# ================================================================
|
|
535
|
+
.section id: "select"
|
|
536
|
+
.section-title "Select"
|
|
537
|
+
span.badge @click: (=> @_viewSource('select')), "184 lines ❐"
|
|
538
|
+
.section-nav
|
|
539
|
+
a @click: (=> @_navSection('select', 'prev')), "‹"
|
|
540
|
+
a @click: (=> @_navSection('select', 'home')), "⌂"
|
|
541
|
+
a @click: (=> @_navSection('select', 'next')), "›"
|
|
542
|
+
.section-desc "Dropdown with typeahead, keyboard nav, ARIA listbox."
|
|
543
|
+
.demo-row
|
|
544
|
+
.demo-label "Basic select"
|
|
545
|
+
Select value <=> fruit, @change: (=> null)
|
|
546
|
+
for f in allFruits
|
|
547
|
+
option value: f.toLowerCase(), f
|
|
548
|
+
.status "selected: #{fruit ?? 'none'}"
|
|
549
|
+
.demo-row
|
|
550
|
+
.demo-label "With placeholder"
|
|
551
|
+
Select value <=> role, placeholder: "Choose a role..."
|
|
552
|
+
option value: "eng", "Engineer"
|
|
553
|
+
option value: "des", "Designer"
|
|
554
|
+
option value: "mgr", "Manager"
|
|
555
|
+
option value: "dir", "Director"
|
|
556
|
+
.status "selected: #{role ?? 'none'}"
|
|
557
|
+
.api
|
|
558
|
+
dl
|
|
559
|
+
dt "Props"
|
|
560
|
+
dd
|
|
561
|
+
code "@value"
|
|
562
|
+
code "@placeholder"
|
|
563
|
+
code "@disabled"
|
|
564
|
+
dt "Events"
|
|
565
|
+
dd
|
|
566
|
+
code "change"
|
|
567
|
+
dt "Keyboard"
|
|
568
|
+
dd
|
|
569
|
+
kbd "↕"
|
|
570
|
+
kbd "Enter"
|
|
571
|
+
kbd "Space"
|
|
572
|
+
kbd "Esc"
|
|
573
|
+
kbd "Home"
|
|
574
|
+
kbd "End"
|
|
575
|
+
kbd "type-ahead"
|
|
576
|
+
dt "Data"
|
|
577
|
+
dd
|
|
578
|
+
code "$open"
|
|
579
|
+
code "$placeholder"
|
|
580
|
+
code "$disabled"
|
|
581
|
+
code "$value"
|
|
582
|
+
code "$highlighted"
|
|
583
|
+
code "$selected"
|
|
584
|
+
|
|
585
|
+
# ================================================================
|
|
586
|
+
# COMBOBOX
|
|
587
|
+
# ================================================================
|
|
588
|
+
.section id: "combobox"
|
|
589
|
+
.section-title "Combobox"
|
|
590
|
+
span.badge @click: (=> @_viewSource('combobox')), "153 lines ❐"
|
|
591
|
+
.section-nav
|
|
592
|
+
a @click: (=> @_navSection('combobox', 'prev')), "‹"
|
|
593
|
+
a @click: (=> @_navSection('combobox', 'home')), "⌂"
|
|
594
|
+
a @click: (=> @_navSection('combobox', 'next')), "›"
|
|
595
|
+
.section-desc "Filterable input + listbox for search-as-you-type."
|
|
596
|
+
.demo-row
|
|
597
|
+
.demo-label "Search fruits"
|
|
598
|
+
Combobox query <=> searchQuery, items: filteredFruits, placeholder: "Type to search..."
|
|
599
|
+
@select: (e) => (searchQuery = e.detail if e.detail?; filteredFruits = allFruits)
|
|
600
|
+
@filter: (=> filteredFruits = allFruits.filter (f) -> f.toLowerCase().includes(searchQuery.toLowerCase()))
|
|
601
|
+
.status "query: '#{searchQuery}'"
|
|
602
|
+
.api
|
|
603
|
+
dl
|
|
604
|
+
dt "Props"
|
|
605
|
+
dd
|
|
606
|
+
code "@query"
|
|
607
|
+
code "@items"
|
|
608
|
+
code "@placeholder"
|
|
609
|
+
code "@disabled"
|
|
610
|
+
code "@autoHighlight"
|
|
611
|
+
dt "Events"
|
|
612
|
+
dd
|
|
613
|
+
code "filter"
|
|
614
|
+
code "select"
|
|
615
|
+
dt "Keyboard"
|
|
616
|
+
dd
|
|
617
|
+
kbd "↕"
|
|
618
|
+
kbd "Enter"
|
|
619
|
+
kbd "Esc"
|
|
620
|
+
kbd "Tab"
|
|
621
|
+
dt "Data"
|
|
622
|
+
dd
|
|
623
|
+
code "$open"
|
|
624
|
+
code "$disabled"
|
|
625
|
+
code "$clear"
|
|
626
|
+
code "$value"
|
|
627
|
+
code "$highlighted"
|
|
628
|
+
code "$empty"
|
|
629
|
+
|
|
630
|
+
# ================================================================
|
|
631
|
+
# MULTI-SELECT
|
|
632
|
+
# ================================================================
|
|
633
|
+
.section id: "multi-select"
|
|
634
|
+
.section-title "MultiSelect"
|
|
635
|
+
span.badge @click: (=> @_viewSource('multi-select')), "158 lines ❐"
|
|
636
|
+
.section-nav
|
|
637
|
+
a @click: (=> @_navSection('multi-select', 'prev')), "‹"
|
|
638
|
+
a @click: (=> @_navSection('multi-select', 'home')), "⌂"
|
|
639
|
+
a @click: (=> @_navSection('multi-select', 'next')), "›"
|
|
640
|
+
.section-desc "Multi-select with chips, filtering, and keyboard navigation."
|
|
641
|
+
.demo-row
|
|
642
|
+
.demo-label "Pick colors"
|
|
643
|
+
MultiSelect value <=> selectedColors, items: ['Red', 'Orange', 'Yellow', 'Green', 'Blue', 'Indigo', 'Violet'], placeholder: "Choose colors..."
|
|
644
|
+
.status "selected: #{selectedColors.join(', ')}"
|
|
645
|
+
.api
|
|
646
|
+
dl
|
|
647
|
+
dt "Props"
|
|
648
|
+
dd
|
|
649
|
+
code "@value"
|
|
650
|
+
code "@items"
|
|
651
|
+
code "@placeholder"
|
|
652
|
+
code "@disabled"
|
|
653
|
+
dt "Events"
|
|
654
|
+
dd
|
|
655
|
+
code "change"
|
|
656
|
+
dt "Keyboard"
|
|
657
|
+
dd
|
|
658
|
+
kbd "↕"
|
|
659
|
+
kbd "Enter"
|
|
660
|
+
kbd "Esc"
|
|
661
|
+
kbd "Backspace"
|
|
662
|
+
kbd "Tab"
|
|
663
|
+
dt "Data"
|
|
664
|
+
dd
|
|
665
|
+
code "$open"
|
|
666
|
+
code "$disabled"
|
|
667
|
+
code "$chips"
|
|
668
|
+
code "$chip"
|
|
669
|
+
code "$remove"
|
|
670
|
+
code "$clear"
|
|
671
|
+
code "$highlighted"
|
|
672
|
+
code "$selected"
|
|
673
|
+
|
|
674
|
+
# ================================================================
|
|
675
|
+
# AUTOCOMPLETE
|
|
676
|
+
# ================================================================
|
|
677
|
+
.section id: "autocomplete"
|
|
678
|
+
.section-title "Autocomplete"
|
|
679
|
+
span.badge @click: (=> @_viewSource('autocomplete')), "141 lines ❐"
|
|
680
|
+
.section-nav
|
|
681
|
+
a @click: (=> @_navSection('autocomplete', 'prev')), "‹"
|
|
682
|
+
a @click: (=> @_navSection('autocomplete', 'home')), "⌂"
|
|
683
|
+
a @click: (=> @_navSection('autocomplete', 'next')), "›"
|
|
684
|
+
.section-desc "Suggestion input — type to filter, select to fill."
|
|
685
|
+
.demo-row
|
|
686
|
+
.demo-label "Search cities"
|
|
687
|
+
Autocomplete value <=> citySearch, items: cities
|
|
688
|
+
.status "value: #{citySearch}"
|
|
689
|
+
.api
|
|
690
|
+
dl
|
|
691
|
+
dt "Props"
|
|
692
|
+
dd
|
|
693
|
+
code "@value"
|
|
694
|
+
code "@items"
|
|
695
|
+
code "@placeholder"
|
|
696
|
+
code "@disabled"
|
|
697
|
+
dt "Events"
|
|
698
|
+
dd
|
|
699
|
+
code "select"
|
|
700
|
+
dt "Keyboard"
|
|
701
|
+
dd
|
|
702
|
+
kbd "↕"
|
|
703
|
+
kbd "Enter"
|
|
704
|
+
kbd "Esc"
|
|
705
|
+
kbd "Tab"
|
|
706
|
+
dt "Data"
|
|
707
|
+
dd
|
|
708
|
+
code "$open"
|
|
709
|
+
code "$disabled"
|
|
710
|
+
code "$clear"
|
|
711
|
+
|
|
712
|
+
# ================================================================
|
|
713
|
+
# CHECKBOX
|
|
714
|
+
# ================================================================
|
|
715
|
+
.section id: "checkbox"
|
|
716
|
+
.section-title "Checkbox"
|
|
717
|
+
span.badge @click: (=> @_viewSource('checkbox')), "33 lines ❐"
|
|
718
|
+
.section-nav
|
|
719
|
+
a @click: (=> @_navSection('checkbox', 'prev')), "‹"
|
|
720
|
+
a @click: (=> @_navSection('checkbox', 'home')), "⌂"
|
|
721
|
+
a @click: (=> @_navSection('checkbox', 'next')), "›"
|
|
722
|
+
.section-desc "Toggle with checkbox or switch semantics. Supports indeterminate."
|
|
723
|
+
.demo-row
|
|
724
|
+
.demo-label "Checkbox (unchecked)"
|
|
725
|
+
Checkbox checked <=> check1, @change: (=> p "Clicked!")
|
|
726
|
+
"Enable notifications"
|
|
727
|
+
.status "checked: #{check1}"
|
|
728
|
+
.demo-row
|
|
729
|
+
.demo-label "Checkbox (pre-checked)"
|
|
730
|
+
Checkbox checked <=> check2
|
|
731
|
+
"Accept terms"
|
|
732
|
+
.status "checked: #{check2}"
|
|
733
|
+
.demo-row
|
|
734
|
+
.demo-label "Switch variant"
|
|
735
|
+
Checkbox checked <=> switch1, switch: true
|
|
736
|
+
"Dark mode"
|
|
737
|
+
.status "on: #{switch1}"
|
|
738
|
+
.api
|
|
739
|
+
dl
|
|
740
|
+
dt "Props"
|
|
741
|
+
dd
|
|
742
|
+
code "@checked"
|
|
743
|
+
code "@disabled"
|
|
744
|
+
code "@indeterminate"
|
|
745
|
+
code "@switch"
|
|
746
|
+
dt "Events"
|
|
747
|
+
dd
|
|
748
|
+
code "change"
|
|
749
|
+
dt "Data"
|
|
750
|
+
dd
|
|
751
|
+
code "$checked"
|
|
752
|
+
code "$indeterminate"
|
|
753
|
+
code "$disabled"
|
|
754
|
+
|
|
755
|
+
# ================================================================
|
|
756
|
+
# TOGGLE
|
|
757
|
+
# ================================================================
|
|
758
|
+
.section id: "toggle"
|
|
759
|
+
.section-title "Toggle"
|
|
760
|
+
span.badge @click: (=> @_viewSource('toggle')), "24 lines ❐"
|
|
761
|
+
.section-nav
|
|
762
|
+
a @click: (=> @_navSection('toggle', 'prev')), "‹"
|
|
763
|
+
a @click: (=> @_navSection('toggle', 'home')), "⌂"
|
|
764
|
+
a @click: (=> @_navSection('toggle', 'next')), "›"
|
|
765
|
+
.section-desc "Stateful toggle button with pressed state."
|
|
766
|
+
.demo-row
|
|
767
|
+
.demo-label "Tap to toggle"
|
|
768
|
+
.toggle-heart
|
|
769
|
+
Toggle pressed <=> isBold
|
|
770
|
+
span.heart (if isBold then "♥" else "♡")
|
|
771
|
+
.api
|
|
772
|
+
dl
|
|
773
|
+
dt "Props"
|
|
774
|
+
dd
|
|
775
|
+
code "@pressed"
|
|
776
|
+
code "@disabled"
|
|
777
|
+
dt "Events"
|
|
778
|
+
dd
|
|
779
|
+
code "change"
|
|
780
|
+
dt "Data"
|
|
781
|
+
dd
|
|
782
|
+
code "$pressed"
|
|
783
|
+
code "$disabled"
|
|
784
|
+
|
|
785
|
+
# ================================================================
|
|
786
|
+
# TOGGLE GROUP
|
|
787
|
+
# ================================================================
|
|
788
|
+
.section id: "toggle-group"
|
|
789
|
+
.section-title "ToggleGroup"
|
|
790
|
+
span.badge @click: (=> @_viewSource('toggle-group')), "78 lines ❐"
|
|
791
|
+
.section-nav
|
|
792
|
+
a @click: (=> @_navSection('toggle-group', 'prev')), "‹"
|
|
793
|
+
a @click: (=> @_navSection('toggle-group', 'home')), "⌂"
|
|
794
|
+
a @click: (=> @_navSection('toggle-group', 'next')), "›"
|
|
795
|
+
.section-desc "Single or multi-select toggle buttons."
|
|
796
|
+
.demo-row
|
|
797
|
+
.demo-label "Text alignment"
|
|
798
|
+
ToggleGroup value <=> alignment
|
|
799
|
+
div $value: "left", "Left"
|
|
800
|
+
div $value: "center", "Center"
|
|
801
|
+
div $value: "right", "Right"
|
|
802
|
+
.status "alignment: #{alignment ?? 'none'}"
|
|
803
|
+
.demo-row
|
|
804
|
+
.demo-label "Multi-select"
|
|
805
|
+
ToggleGroup value <=> toppings, multiple: true
|
|
806
|
+
div $value: "Cheese", "Cheese"
|
|
807
|
+
div $value: "Bacon", "Bacon"
|
|
808
|
+
div $value: "Lettuce", "Lettuce"
|
|
809
|
+
.status "toppings: #{toppings.join(', ')}"
|
|
810
|
+
.api
|
|
811
|
+
dl
|
|
812
|
+
dt "Props"
|
|
813
|
+
dd
|
|
814
|
+
code "@value"
|
|
815
|
+
code "@disabled"
|
|
816
|
+
code "@multiple"
|
|
817
|
+
code "@orientation"
|
|
818
|
+
dt "Events"
|
|
819
|
+
dd
|
|
820
|
+
code "change"
|
|
821
|
+
dt "Keyboard"
|
|
822
|
+
dd
|
|
823
|
+
kbd "← →"
|
|
824
|
+
kbd "↕"
|
|
825
|
+
kbd "Home"
|
|
826
|
+
kbd "End"
|
|
827
|
+
dt "Data"
|
|
828
|
+
dd
|
|
829
|
+
code "$orientation"
|
|
830
|
+
code "$disabled"
|
|
831
|
+
code "$pressed"
|
|
832
|
+
code "$value"
|
|
833
|
+
|
|
834
|
+
# ================================================================
|
|
835
|
+
# RADIO GROUP
|
|
836
|
+
# ================================================================
|
|
837
|
+
.section id: "radio-group"
|
|
838
|
+
.section-title "RadioGroup"
|
|
839
|
+
span.badge @click: (=> @_viewSource('radio-group')), "67 lines ❐"
|
|
840
|
+
.section-nav
|
|
841
|
+
a @click: (=> @_navSection('radio-group', 'prev')), "‹"
|
|
842
|
+
a @click: (=> @_navSection('radio-group', 'home')), "⌂"
|
|
843
|
+
a @click: (=> @_navSection('radio-group', 'next')), "›"
|
|
844
|
+
.section-desc "Exactly one option selected. Arrow keys move focus and selection."
|
|
845
|
+
.demo-row
|
|
846
|
+
.demo-label "Font size"
|
|
847
|
+
RadioGroup value <=> fontSize
|
|
848
|
+
div $value: "sm", "Small"
|
|
849
|
+
div $value: "md", "Medium"
|
|
850
|
+
div $value: "lg", "Large"
|
|
851
|
+
.status "fontSize: #{fontSize ?? 'none'}"
|
|
852
|
+
.api
|
|
853
|
+
dl
|
|
854
|
+
dt "Props"
|
|
855
|
+
dd
|
|
856
|
+
code "@value"
|
|
857
|
+
code "@disabled"
|
|
858
|
+
code "@orientation"
|
|
859
|
+
code "@name"
|
|
860
|
+
dt "Events"
|
|
861
|
+
dd
|
|
862
|
+
code "change"
|
|
863
|
+
dt "Keyboard"
|
|
864
|
+
dd
|
|
865
|
+
kbd "← →"
|
|
866
|
+
kbd "↕"
|
|
867
|
+
kbd "Home"
|
|
868
|
+
kbd "End"
|
|
869
|
+
dt "Data"
|
|
870
|
+
dd
|
|
871
|
+
code "$orientation"
|
|
872
|
+
code "$disabled"
|
|
873
|
+
code "$checked"
|
|
874
|
+
code "$value"
|
|
875
|
+
|
|
876
|
+
# ================================================================
|
|
877
|
+
# CHECKBOX GROUP
|
|
878
|
+
# ================================================================
|
|
879
|
+
.section id: "checkbox-group"
|
|
880
|
+
.section-title "CheckboxGroup"
|
|
881
|
+
span.badge @click: (=> @_viewSource('checkbox-group')), "65 lines ❐"
|
|
882
|
+
.section-nav
|
|
883
|
+
a @click: (=> @_navSection('checkbox-group', 'prev')), "‹"
|
|
884
|
+
a @click: (=> @_navSection('checkbox-group', 'home')), "⌂"
|
|
885
|
+
a @click: (=> @_navSection('checkbox-group', 'next')), "›"
|
|
886
|
+
.section-desc "Multiple options checked independently."
|
|
887
|
+
.demo-row
|
|
888
|
+
.demo-label "Toppings"
|
|
889
|
+
CheckboxGroup value <=> cbToppings, label: "Pizza toppings"
|
|
890
|
+
div $value: "Cheese", "Cheese"
|
|
891
|
+
div $value: "Bacon", "Bacon"
|
|
892
|
+
div $value: "Lettuce", "Lettuce"
|
|
893
|
+
div $value: "Tomato", "Tomato"
|
|
894
|
+
.status "selected: #{cbToppings.join(', ')}"
|
|
895
|
+
.api
|
|
896
|
+
dl
|
|
897
|
+
dt "Props"
|
|
898
|
+
dd
|
|
899
|
+
code "@value"
|
|
900
|
+
code "@disabled"
|
|
901
|
+
code "@orientation"
|
|
902
|
+
code "@label"
|
|
903
|
+
dt "Events"
|
|
904
|
+
dd
|
|
905
|
+
code "change"
|
|
906
|
+
dt "Keyboard"
|
|
907
|
+
dd
|
|
908
|
+
kbd "← →"
|
|
909
|
+
kbd "↕"
|
|
910
|
+
dt "Data"
|
|
911
|
+
dd
|
|
912
|
+
code "$orientation"
|
|
913
|
+
code "$disabled"
|
|
914
|
+
code "$checked"
|
|
915
|
+
code "$value"
|
|
916
|
+
|
|
917
|
+
# ================================================================
|
|
918
|
+
# INPUT
|
|
919
|
+
# ================================================================
|
|
920
|
+
.section id: "input"
|
|
921
|
+
.section-title "Input"
|
|
922
|
+
span.badge @click: (=> @_viewSource('input')), "35 lines ❐"
|
|
923
|
+
.section-nav
|
|
924
|
+
a @click: (=> @_navSection('input', 'prev')), "‹"
|
|
925
|
+
a @click: (=> @_navSection('input', 'home')), "⌂"
|
|
926
|
+
a @click: (=> @_navSection('input', 'next')), "›"
|
|
927
|
+
.section-desc "Headless input tracking focus, touch, and validation state."
|
|
928
|
+
.demo-row
|
|
929
|
+
.demo-label "Text input"
|
|
930
|
+
Input value <=> userName, placeholder: "Enter your name"
|
|
931
|
+
.status "value: #{userName}"
|
|
932
|
+
.api
|
|
933
|
+
dl
|
|
934
|
+
dt "Props"
|
|
935
|
+
dd
|
|
936
|
+
code "@value"
|
|
937
|
+
code "@placeholder"
|
|
938
|
+
code "@type"
|
|
939
|
+
code "@disabled"
|
|
940
|
+
code "@required"
|
|
941
|
+
dt "Data"
|
|
942
|
+
dd
|
|
943
|
+
code "$disabled"
|
|
944
|
+
code "$focused"
|
|
945
|
+
code "$touched"
|
|
946
|
+
|
|
947
|
+
# ================================================================
|
|
948
|
+
# NUMBER FIELD
|
|
949
|
+
# ================================================================
|
|
950
|
+
.section id: "number-field"
|
|
951
|
+
.section-title "NumberField"
|
|
952
|
+
span.badge @click: (=> @_viewSource('number-field')), "162 lines ❐"
|
|
953
|
+
.section-nav
|
|
954
|
+
a @click: (=> @_navSection('number-field', 'prev')), "‹"
|
|
955
|
+
a @click: (=> @_navSection('number-field', 'home')), "⌂"
|
|
956
|
+
a @click: (=> @_navSection('number-field', 'next')), "›"
|
|
957
|
+
.section-desc "Number input with increment/decrement buttons, hold-to-repeat, and keyboard stepping."
|
|
958
|
+
.demo-row
|
|
959
|
+
.demo-label "Quantity (1-99)"
|
|
960
|
+
.nf-wrap
|
|
961
|
+
NumberField value <=> quantity, min: 1, max: 99
|
|
962
|
+
.status "quantity: #{quantity}"
|
|
963
|
+
.demo-row
|
|
964
|
+
.demo-label "Price (step: 0.01)"
|
|
965
|
+
.nf-wrap
|
|
966
|
+
NumberField value <=> price, min: 0, max: 999.99, step: 0.01
|
|
967
|
+
.status "price: #{price}"
|
|
968
|
+
.api
|
|
969
|
+
dl
|
|
970
|
+
dt "Props"
|
|
971
|
+
dd
|
|
972
|
+
code "@value"
|
|
973
|
+
code "@min"
|
|
974
|
+
code "@max"
|
|
975
|
+
code "@step"
|
|
976
|
+
code "@disabled"
|
|
977
|
+
code "@readOnly"
|
|
978
|
+
dt "Events"
|
|
979
|
+
dd
|
|
980
|
+
code "input"
|
|
981
|
+
code "change"
|
|
982
|
+
dt "Keyboard"
|
|
983
|
+
dd
|
|
984
|
+
kbd "↕"
|
|
985
|
+
kbd "PgUp"
|
|
986
|
+
kbd "PgDn"
|
|
987
|
+
kbd "Home"
|
|
988
|
+
kbd "End"
|
|
989
|
+
dt "Data"
|
|
990
|
+
dd
|
|
991
|
+
code "$disabled"
|
|
992
|
+
code "$readonly"
|
|
993
|
+
code "$decrement"
|
|
994
|
+
code "$increment"
|
|
995
|
+
|
|
996
|
+
# ================================================================
|
|
997
|
+
# SLIDER
|
|
998
|
+
# ================================================================
|
|
999
|
+
.section id: "slider"
|
|
1000
|
+
.section-title "Slider"
|
|
1001
|
+
span.badge @click: (=> @_viewSource('slider')), "165 lines ❐"
|
|
1002
|
+
.section-nav
|
|
1003
|
+
a @click: (=> @_navSection('slider', 'prev')), "‹"
|
|
1004
|
+
a @click: (=> @_navSection('slider', 'home')), "⌂"
|
|
1005
|
+
a @click: (=> @_navSection('slider', 'next')), "›"
|
|
1006
|
+
.section-desc "Draggable range input with pointer capture and keyboard stepping."
|
|
1007
|
+
.demo-row
|
|
1008
|
+
.demo-label "Single thumb"
|
|
1009
|
+
.slider-wrap
|
|
1010
|
+
Slider value <=> sliderVal
|
|
1011
|
+
.status "value: #{sliderVal}"
|
|
1012
|
+
.demo-row
|
|
1013
|
+
.demo-label "Range (two thumbs)"
|
|
1014
|
+
.slider-wrap
|
|
1015
|
+
Slider value <=> rangeVal
|
|
1016
|
+
.status "range: #{rangeVal}"
|
|
1017
|
+
.api
|
|
1018
|
+
dl
|
|
1019
|
+
dt "Props"
|
|
1020
|
+
dd
|
|
1021
|
+
code "@value"
|
|
1022
|
+
code "@min"
|
|
1023
|
+
code "@max"
|
|
1024
|
+
code "@step"
|
|
1025
|
+
code "@orientation"
|
|
1026
|
+
code "@disabled"
|
|
1027
|
+
dt "Events"
|
|
1028
|
+
dd
|
|
1029
|
+
code "input"
|
|
1030
|
+
code "change"
|
|
1031
|
+
dt "Keyboard"
|
|
1032
|
+
dd
|
|
1033
|
+
kbd "← →"
|
|
1034
|
+
kbd "↕"
|
|
1035
|
+
kbd "PgUp"
|
|
1036
|
+
kbd "PgDn"
|
|
1037
|
+
kbd "Home"
|
|
1038
|
+
kbd "End"
|
|
1039
|
+
dt "Data"
|
|
1040
|
+
dd
|
|
1041
|
+
code "$orientation"
|
|
1042
|
+
code "$disabled"
|
|
1043
|
+
code "$dragging"
|
|
1044
|
+
code "$track"
|
|
1045
|
+
code "$indicator"
|
|
1046
|
+
code "$thumb"
|
|
1047
|
+
code "$active"
|
|
1048
|
+
|
|
1049
|
+
# ================================================================
|
|
1050
|
+
# OTP FIELD
|
|
1051
|
+
# ================================================================
|
|
1052
|
+
.section id: "otp-field"
|
|
1053
|
+
.section-title "OTPField"
|
|
1054
|
+
span.badge @click: (=> @_viewSource('otp-field')), "89 lines ❐"
|
|
1055
|
+
.section-nav
|
|
1056
|
+
a @click: (=> @_navSection('otp-field', 'prev')), "‹"
|
|
1057
|
+
a @click: (=> @_navSection('otp-field', 'home')), "⌂"
|
|
1058
|
+
a @click: (=> @_navSection('otp-field', 'next')), "›"
|
|
1059
|
+
.section-desc "Multi-digit code input with auto-advance, backspace nav, and paste."
|
|
1060
|
+
.demo-row
|
|
1061
|
+
.demo-label "6-digit code"
|
|
1062
|
+
OTPField length: 6, value <=> otpCode
|
|
1063
|
+
.status "code: '#{otpCode}'"
|
|
1064
|
+
.demo-row
|
|
1065
|
+
.demo-label "4-digit masked"
|
|
1066
|
+
OTPField length: 4, mask: true
|
|
1067
|
+
.api
|
|
1068
|
+
dl
|
|
1069
|
+
dt "Props"
|
|
1070
|
+
dd
|
|
1071
|
+
code "@length"
|
|
1072
|
+
code "@value"
|
|
1073
|
+
code "@disabled"
|
|
1074
|
+
code "@mask"
|
|
1075
|
+
dt "Events"
|
|
1076
|
+
dd
|
|
1077
|
+
code "input"
|
|
1078
|
+
code "complete"
|
|
1079
|
+
dt "Keyboard"
|
|
1080
|
+
dd
|
|
1081
|
+
kbd "← →"
|
|
1082
|
+
kbd "Backspace"
|
|
1083
|
+
kbd "Home"
|
|
1084
|
+
kbd "End"
|
|
1085
|
+
kbd "paste"
|
|
1086
|
+
dt "Data"
|
|
1087
|
+
dd
|
|
1088
|
+
code "$disabled"
|
|
1089
|
+
code "$complete"
|
|
1090
|
+
code "$filled"
|
|
1091
|
+
|
|
1092
|
+
# ================================================================
|
|
1093
|
+
# DATE PICKER
|
|
1094
|
+
# ================================================================
|
|
1095
|
+
.section id: "date-picker"
|
|
1096
|
+
.section-title "DatePicker"
|
|
1097
|
+
span.badge @click: (=> @_viewSource('date-picker')), "214 lines ❐"
|
|
1098
|
+
.section-nav
|
|
1099
|
+
a @click: (=> @_navSection('date-picker', 'prev')), "‹"
|
|
1100
|
+
a @click: (=> @_navSection('date-picker', 'home')), "⌂"
|
|
1101
|
+
a @click: (=> @_navSection('date-picker', 'next')), "›"
|
|
1102
|
+
.section-desc "Calendar dropdown for single date or range selection."
|
|
1103
|
+
.demo-row
|
|
1104
|
+
.demo-label "Single date"
|
|
1105
|
+
DatePicker value <=> pickedDate
|
|
1106
|
+
.status "date: #{if pickedDate then pickedDate.toLocaleDateString() else 'none'}"
|
|
1107
|
+
.demo-row
|
|
1108
|
+
.demo-label "Date range"
|
|
1109
|
+
DatePicker value <=> dateRange, range: true
|
|
1110
|
+
.status "range: #{if dateRange and dateRange[0] then dateRange[0].toLocaleDateString() else 'none'} – #{if dateRange and dateRange[1] then dateRange[1].toLocaleDateString() else '...'}"
|
|
1111
|
+
.api
|
|
1112
|
+
dl
|
|
1113
|
+
dt "Props"
|
|
1114
|
+
dd
|
|
1115
|
+
code "@value"
|
|
1116
|
+
code "@placeholder"
|
|
1117
|
+
code "@disabled"
|
|
1118
|
+
code "@range"
|
|
1119
|
+
code "@firstDayOfWeek"
|
|
1120
|
+
dt "Events"
|
|
1121
|
+
dd
|
|
1122
|
+
code "change"
|
|
1123
|
+
dt "Keyboard"
|
|
1124
|
+
dd
|
|
1125
|
+
kbd "Esc"
|
|
1126
|
+
kbd "Enter"
|
|
1127
|
+
kbd "Space"
|
|
1128
|
+
dt "Data"
|
|
1129
|
+
dd
|
|
1130
|
+
code "$open"
|
|
1131
|
+
code "$disabled"
|
|
1132
|
+
code "$range"
|
|
1133
|
+
code "$calendar"
|
|
1134
|
+
code "$selected"
|
|
1135
|
+
code "$today"
|
|
1136
|
+
code "$in-range"
|
|
1137
|
+
|
|
1138
|
+
# ================================================================
|
|
1139
|
+
# EDITABLE VALUE
|
|
1140
|
+
# ================================================================
|
|
1141
|
+
.section id: "editable-value"
|
|
1142
|
+
.section-title "EditableValue"
|
|
1143
|
+
span.badge @click: (=> @_viewSource('editable-value')), "80 lines ❐"
|
|
1144
|
+
.section-nav
|
|
1145
|
+
a @click: (=> @_navSection('editable-value', 'prev')), "‹"
|
|
1146
|
+
a @click: (=> @_navSection('editable-value', 'home')), "⌂"
|
|
1147
|
+
a @click: (=> @_navSection('editable-value', 'next')), "›"
|
|
1148
|
+
.section-desc "Click the edit icon to modify the value inline."
|
|
1149
|
+
.demo-row
|
|
1150
|
+
EditableValue @save: (=> editName = editName)
|
|
1151
|
+
span $display: true
|
|
1152
|
+
editName
|
|
1153
|
+
div $editor: true, hidden: true
|
|
1154
|
+
input type: "text", value: editName
|
|
1155
|
+
@input: (e) => editName = e.target.value
|
|
1156
|
+
.api
|
|
1157
|
+
dl
|
|
1158
|
+
dt "Props"
|
|
1159
|
+
dd
|
|
1160
|
+
code "@disabled"
|
|
1161
|
+
dt "Events"
|
|
1162
|
+
dd
|
|
1163
|
+
code "save"
|
|
1164
|
+
dt "Keyboard"
|
|
1165
|
+
dd
|
|
1166
|
+
kbd "Esc"
|
|
1167
|
+
kbd "Enter"
|
|
1168
|
+
dt "Data"
|
|
1169
|
+
dd
|
|
1170
|
+
code "$editing"
|
|
1171
|
+
code "$disabled"
|
|
1172
|
+
code "$saving"
|
|
1173
|
+
code "$edit-trigger"
|
|
1174
|
+
|
|
1175
|
+
# ================================================================
|
|
1176
|
+
# TABS
|
|
1177
|
+
# ================================================================
|
|
1178
|
+
.section id: "tabs"
|
|
1179
|
+
.section-title "Tabs"
|
|
1180
|
+
span.badge @click: (=> @_viewSource('tabs')), "124 lines ❐"
|
|
1181
|
+
.section-nav
|
|
1182
|
+
a @click: (=> @_navSection('tabs', 'prev')), "‹"
|
|
1183
|
+
a @click: (=> @_navSection('tabs', 'home')), "⌂"
|
|
1184
|
+
a @click: (=> @_navSection('tabs', 'next')), "›"
|
|
1185
|
+
.section-desc "Tab panel with roving tabindex and arrow key navigation."
|
|
1186
|
+
.demo-row
|
|
1187
|
+
.demo-label "Basic tabs"
|
|
1188
|
+
Tabs active <=> currentTab
|
|
1189
|
+
div data-tab: "overview", "Overview"
|
|
1190
|
+
div data-tab: "details", "Details"
|
|
1191
|
+
div data-tab: "settings", "Settings"
|
|
1192
|
+
div data-panel: "overview"
|
|
1193
|
+
p "This is the overview panel."
|
|
1194
|
+
div data-panel: "details"
|
|
1195
|
+
p "These are the details."
|
|
1196
|
+
div data-panel: "settings"
|
|
1197
|
+
p "Settings go here."
|
|
1198
|
+
.status "active: #{currentTab}"
|
|
1199
|
+
.api
|
|
1200
|
+
dl
|
|
1201
|
+
dt "Props"
|
|
1202
|
+
dd
|
|
1203
|
+
code "@active"
|
|
1204
|
+
code "@orientation"
|
|
1205
|
+
code "@activation"
|
|
1206
|
+
dt "Events"
|
|
1207
|
+
dd
|
|
1208
|
+
code "change"
|
|
1209
|
+
dt "Keyboard"
|
|
1210
|
+
dd
|
|
1211
|
+
kbd "← →"
|
|
1212
|
+
kbd "↕"
|
|
1213
|
+
kbd "Home"
|
|
1214
|
+
kbd "End"
|
|
1215
|
+
kbd "Enter"
|
|
1216
|
+
kbd "Space"
|
|
1217
|
+
dt "Data"
|
|
1218
|
+
dd
|
|
1219
|
+
code "$tab"
|
|
1220
|
+
code "$panel"
|
|
1221
|
+
code "$active"
|
|
1222
|
+
code "$disabled"
|
|
1223
|
+
|
|
1224
|
+
# ================================================================
|
|
1225
|
+
# MENU
|
|
1226
|
+
# ================================================================
|
|
1227
|
+
.section id: "menu"
|
|
1228
|
+
.section-title "Menu"
|
|
1229
|
+
span.badge @click: (=> @_viewSource('menu')), "162 lines ❐"
|
|
1230
|
+
.section-nav
|
|
1231
|
+
a @click: (=> @_navSection('menu', 'prev')), "‹"
|
|
1232
|
+
a @click: (=> @_navSection('menu', 'home')), "⌂"
|
|
1233
|
+
a @click: (=> @_navSection('menu', 'next')), "›"
|
|
1234
|
+
.section-desc "Dropdown action menu with keyboard navigation."
|
|
1235
|
+
.demo-row
|
|
1236
|
+
.demo-label "Click to open menu"
|
|
1237
|
+
Menu
|
|
1238
|
+
@select: (e) => console.log("Selected: #{e.detail}")
|
|
1239
|
+
span "Actions"
|
|
1240
|
+
div data-item: "edit", "Edit"
|
|
1241
|
+
div data-item: "duplicate", "Duplicate"
|
|
1242
|
+
div data-item: "archive", "Archive"
|
|
1243
|
+
div data-item: "delete", "Delete"
|
|
1244
|
+
.api
|
|
1245
|
+
dl
|
|
1246
|
+
dt "Props"
|
|
1247
|
+
dd
|
|
1248
|
+
code "@disabled"
|
|
1249
|
+
dt "Events"
|
|
1250
|
+
dd
|
|
1251
|
+
code "select"
|
|
1252
|
+
dt "Keyboard"
|
|
1253
|
+
dd
|
|
1254
|
+
kbd "↕"
|
|
1255
|
+
kbd "Home"
|
|
1256
|
+
kbd "End"
|
|
1257
|
+
kbd "Enter"
|
|
1258
|
+
kbd "Space"
|
|
1259
|
+
kbd "Esc"
|
|
1260
|
+
kbd "Tab"
|
|
1261
|
+
kbd "type-ahead"
|
|
1262
|
+
dt "Data"
|
|
1263
|
+
dd
|
|
1264
|
+
code "$open"
|
|
1265
|
+
code "$disabled"
|
|
1266
|
+
code "$highlighted"
|
|
1267
|
+
code "$value"
|
|
1268
|
+
|
|
1269
|
+
# ================================================================
|
|
1270
|
+
# CONTEXT MENU
|
|
1271
|
+
# ================================================================
|
|
1272
|
+
.section id: "context-menu"
|
|
1273
|
+
.section-title "ContextMenu"
|
|
1274
|
+
span.badge @click: (=> @_viewSource('context-menu')), "98 lines ❐"
|
|
1275
|
+
.section-nav
|
|
1276
|
+
a @click: (=> @_navSection('context-menu', 'prev')), "‹"
|
|
1277
|
+
a @click: (=> @_navSection('context-menu', 'home')), "⌂"
|
|
1278
|
+
a @click: (=> @_navSection('context-menu', 'next')), "›"
|
|
1279
|
+
.section-desc "Right-click the dashed area to open a context menu."
|
|
1280
|
+
ContextMenu
|
|
1281
|
+
div $trigger: true
|
|
1282
|
+
.context-zone
|
|
1283
|
+
"Right-click here"
|
|
1284
|
+
div $item: "cut", "Cut"
|
|
1285
|
+
div $item: "copy", "Copy"
|
|
1286
|
+
div $item: "paste", "Paste"
|
|
1287
|
+
.api
|
|
1288
|
+
dl
|
|
1289
|
+
dt "Props"
|
|
1290
|
+
dd
|
|
1291
|
+
code "@disabled"
|
|
1292
|
+
dt "Events"
|
|
1293
|
+
dd
|
|
1294
|
+
code "select"
|
|
1295
|
+
dt "Keyboard"
|
|
1296
|
+
dd
|
|
1297
|
+
kbd "↕"
|
|
1298
|
+
kbd "Home"
|
|
1299
|
+
kbd "End"
|
|
1300
|
+
kbd "Enter"
|
|
1301
|
+
kbd "Space"
|
|
1302
|
+
kbd "Esc"
|
|
1303
|
+
kbd "Tab"
|
|
1304
|
+
dt "Data"
|
|
1305
|
+
dd
|
|
1306
|
+
code "$open"
|
|
1307
|
+
code "$highlighted"
|
|
1308
|
+
code "$disabled"
|
|
1309
|
+
code "$value"
|
|
1310
|
+
|
|
1311
|
+
# ================================================================
|
|
1312
|
+
# MENUBAR
|
|
1313
|
+
# ================================================================
|
|
1314
|
+
.section id: "menubar"
|
|
1315
|
+
.section-title "Menubar"
|
|
1316
|
+
span.badge @click: (=> @_viewSource('menubar')), "155 lines ❐"
|
|
1317
|
+
.section-nav
|
|
1318
|
+
a @click: (=> @_navSection('menubar', 'prev')), "‹"
|
|
1319
|
+
a @click: (=> @_navSection('menubar', 'home')), "⌂"
|
|
1320
|
+
a @click: (=> @_navSection('menubar', 'next')), "›"
|
|
1321
|
+
.section-desc "Horizontal menu bar with dropdown menus."
|
|
1322
|
+
.demo-row
|
|
1323
|
+
Menubar
|
|
1324
|
+
div $menu: "File"
|
|
1325
|
+
div $item: "new", "New"
|
|
1326
|
+
div $item: "open", "Open"
|
|
1327
|
+
div $item: "save", "Save"
|
|
1328
|
+
div $menu: "Edit"
|
|
1329
|
+
div $item: "undo", "Undo"
|
|
1330
|
+
div $item: "redo", "Redo"
|
|
1331
|
+
div $item: "cut", "Cut"
|
|
1332
|
+
div $menu: "View"
|
|
1333
|
+
div $item: "zoom-in", "Zoom In"
|
|
1334
|
+
div $item: "zoom-out", "Zoom Out"
|
|
1335
|
+
.api
|
|
1336
|
+
dl
|
|
1337
|
+
dt "Props"
|
|
1338
|
+
dd
|
|
1339
|
+
code "@disabled"
|
|
1340
|
+
dt "Events"
|
|
1341
|
+
dd
|
|
1342
|
+
code "select"
|
|
1343
|
+
dt "Keyboard"
|
|
1344
|
+
dd
|
|
1345
|
+
kbd "← →"
|
|
1346
|
+
kbd "↕"
|
|
1347
|
+
kbd "Enter"
|
|
1348
|
+
kbd "Space"
|
|
1349
|
+
kbd "Esc"
|
|
1350
|
+
kbd "Tab"
|
|
1351
|
+
dt "Data"
|
|
1352
|
+
dd
|
|
1353
|
+
code "$disabled"
|
|
1354
|
+
code "$open"
|
|
1355
|
+
code "$highlighted"
|
|
1356
|
+
code "$value"
|
|
1357
|
+
|
|
1358
|
+
# ================================================================
|
|
1359
|
+
# NAVIGATION MENU
|
|
1360
|
+
# ================================================================
|
|
1361
|
+
.section id: "nav-menu"
|
|
1362
|
+
.section-title "NavigationMenu"
|
|
1363
|
+
span.badge @click: (=> @_viewSource('nav-menu')), "132 lines ❐"
|
|
1364
|
+
.section-nav
|
|
1365
|
+
a @click: (=> @_navSection('nav-menu', 'prev')), "‹"
|
|
1366
|
+
a @click: (=> @_navSection('nav-menu', 'home')), "⌂"
|
|
1367
|
+
a @click: (=> @_navSection('nav-menu', 'next')), "›"
|
|
1368
|
+
.section-desc "Site navigation with dropdown panels."
|
|
1369
|
+
.demo-row
|
|
1370
|
+
NavigationMenu
|
|
1371
|
+
a $link: true, href: "javascript:void(0)"
|
|
1372
|
+
"Home"
|
|
1373
|
+
div $trigger: "Products"
|
|
1374
|
+
div $panel: true
|
|
1375
|
+
a href: "javascript:void(0)", "Components"
|
|
1376
|
+
a href: "javascript:void(0)", "Templates"
|
|
1377
|
+
a href: "javascript:void(0)", "Playground"
|
|
1378
|
+
div $trigger: "Learn"
|
|
1379
|
+
div $panel: true
|
|
1380
|
+
a href: "javascript:void(0)", "Documentation"
|
|
1381
|
+
a href: "javascript:void(0)", "Blog"
|
|
1382
|
+
a href: "javascript:void(0)", "Changelog"
|
|
1383
|
+
a $link: true, href: "javascript:void(0)"
|
|
1384
|
+
"GitHub"
|
|
1385
|
+
.api
|
|
1386
|
+
dl
|
|
1387
|
+
dt "Props"
|
|
1388
|
+
dd
|
|
1389
|
+
code "@orientation"
|
|
1390
|
+
code "@hoverDelay"
|
|
1391
|
+
code "@hoverCloseDelay"
|
|
1392
|
+
dt "Keyboard"
|
|
1393
|
+
dd
|
|
1394
|
+
kbd "← →"
|
|
1395
|
+
kbd "↓"
|
|
1396
|
+
kbd "Esc"
|
|
1397
|
+
dt "Data"
|
|
1398
|
+
dd
|
|
1399
|
+
code "$orientation"
|
|
1400
|
+
code "$open"
|
|
1401
|
+
|
|
1402
|
+
# ================================================================
|
|
1403
|
+
# TOOLBAR
|
|
1404
|
+
# ================================================================
|
|
1405
|
+
.section id: "toolbar"
|
|
1406
|
+
.section-title "Toolbar"
|
|
1407
|
+
span.badge @click: (=> @_viewSource('toolbar')), "46 lines ❐"
|
|
1408
|
+
.section-nav
|
|
1409
|
+
a @click: (=> @_navSection('toolbar', 'prev')), "‹"
|
|
1410
|
+
a @click: (=> @_navSection('toolbar', 'home')), "⌂"
|
|
1411
|
+
a @click: (=> @_navSection('toolbar', 'next')), "›"
|
|
1412
|
+
.section-desc "Groups controls with roving tabindex keyboard navigation."
|
|
1413
|
+
.demo-row
|
|
1414
|
+
.demo-label "Text"
|
|
1415
|
+
Toolbar label: "Formatting"
|
|
1416
|
+
Button
|
|
1417
|
+
"Save"
|
|
1418
|
+
Button
|
|
1419
|
+
"Undo"
|
|
1420
|
+
Separator orientation: "vertical"
|
|
1421
|
+
Toggle pressed <=> isBold
|
|
1422
|
+
"Bold"
|
|
1423
|
+
Toggle pressed <=> isItalic
|
|
1424
|
+
"Italic"
|
|
1425
|
+
.demo-row
|
|
1426
|
+
.demo-label "Icons + Text"
|
|
1427
|
+
Toolbar label: "Formatting"
|
|
1428
|
+
Button
|
|
1429
|
+
"💾 Save"
|
|
1430
|
+
Button
|
|
1431
|
+
"↩ Undo"
|
|
1432
|
+
Separator orientation: "vertical"
|
|
1433
|
+
Toggle pressed <=> isBold
|
|
1434
|
+
"𝐁 Bold"
|
|
1435
|
+
Toggle pressed <=> isItalic
|
|
1436
|
+
"𝐼 Italic"
|
|
1437
|
+
.demo-row
|
|
1438
|
+
.demo-label "Icons Only"
|
|
1439
|
+
Toolbar label: "Formatting"
|
|
1440
|
+
Button aria-label: "Save"
|
|
1441
|
+
"💾"
|
|
1442
|
+
Button aria-label: "Undo"
|
|
1443
|
+
"↩"
|
|
1444
|
+
Separator orientation: "vertical"
|
|
1445
|
+
Toggle pressed <=> isBold, aria-label: "Bold"
|
|
1446
|
+
"𝐁"
|
|
1447
|
+
Toggle pressed <=> isItalic, aria-label: "Italic"
|
|
1448
|
+
"𝐼"
|
|
1449
|
+
.api
|
|
1450
|
+
dl
|
|
1451
|
+
dt "Props"
|
|
1452
|
+
dd
|
|
1453
|
+
code "@orientation"
|
|
1454
|
+
code "@label"
|
|
1455
|
+
dt "Keyboard"
|
|
1456
|
+
dd
|
|
1457
|
+
kbd "← →"
|
|
1458
|
+
kbd "↕"
|
|
1459
|
+
kbd "Home"
|
|
1460
|
+
kbd "End"
|
|
1461
|
+
dt "Data"
|
|
1462
|
+
dd
|
|
1463
|
+
code "$orientation"
|
|
1464
|
+
|
|
1465
|
+
# ================================================================
|
|
1466
|
+
# DIALOG
|
|
1467
|
+
# ================================================================
|
|
1468
|
+
.section id: "dialog"
|
|
1469
|
+
.section-title "Dialog"
|
|
1470
|
+
span.badge @click: (=> @_viewSource('dialog')), "107 lines ❐"
|
|
1471
|
+
.section-nav
|
|
1472
|
+
a @click: (=> @_navSection('dialog', 'prev')), "‹"
|
|
1473
|
+
a @click: (=> @_navSection('dialog', 'home')), "⌂"
|
|
1474
|
+
a @click: (=> @_navSection('dialog', 'next')), "›"
|
|
1475
|
+
.section-desc "Modal with focus trap, scroll lock, escape/click-outside dismiss."
|
|
1476
|
+
.demo-row
|
|
1477
|
+
.demo-label "Click to open"
|
|
1478
|
+
button.demo-btn @click: (=> showDialog = true)
|
|
1479
|
+
"Open Dialog"
|
|
1480
|
+
.status "open: #{showDialog}"
|
|
1481
|
+
Dialog open <=> showDialog, @close: (=> showDialog = false)
|
|
1482
|
+
.dialog-panel
|
|
1483
|
+
h2 style: "font-size:18px;font-weight:600;margin-bottom:8px", "Confirm Action"
|
|
1484
|
+
p.dialog-desc "Are you sure you want to proceed?"
|
|
1485
|
+
. style: "display:flex;gap:8px;justify-content:flex-end"
|
|
1486
|
+
button.demo-btn @click: (=> showDialog = false)
|
|
1487
|
+
"Cancel"
|
|
1488
|
+
button style: "padding:6px 16px;border:none;border-radius:6px;background:#1d4ed8;color:white;font-weight:600", @click: (=> showDialog = false)
|
|
1489
|
+
"Confirm"
|
|
1490
|
+
.api
|
|
1491
|
+
dl
|
|
1492
|
+
dt "Props"
|
|
1493
|
+
dd
|
|
1494
|
+
code "@open"
|
|
1495
|
+
code "@dismissable"
|
|
1496
|
+
code "@initialFocus"
|
|
1497
|
+
dt "Events"
|
|
1498
|
+
dd
|
|
1499
|
+
code "close"
|
|
1500
|
+
dt "Keyboard"
|
|
1501
|
+
dd
|
|
1502
|
+
kbd "Esc"
|
|
1503
|
+
kbd "Tab"
|
|
1504
|
+
dt "Data"
|
|
1505
|
+
dd
|
|
1506
|
+
code "$open"
|
|
1507
|
+
|
|
1508
|
+
# ================================================================
|
|
1509
|
+
# DRAWER
|
|
1510
|
+
# ================================================================
|
|
1511
|
+
.section id: "drawer"
|
|
1512
|
+
.section-title "Drawer"
|
|
1513
|
+
span.badge @click: (=> @_viewSource('drawer')), "79 lines ❐"
|
|
1514
|
+
.section-nav
|
|
1515
|
+
a @click: (=> @_navSection('drawer', 'prev')), "‹"
|
|
1516
|
+
a @click: (=> @_navSection('drawer', 'home')), "⌂"
|
|
1517
|
+
a @click: (=> @_navSection('drawer', 'next')), "›"
|
|
1518
|
+
.section-desc "Slide-out panel from edge of screen with focus trap and scroll lock."
|
|
1519
|
+
.demo-row
|
|
1520
|
+
.demo-label "Open from right"
|
|
1521
|
+
.btn-demo
|
|
1522
|
+
.btn-row
|
|
1523
|
+
button @click: (=> showDrawer = true)
|
|
1524
|
+
"Open Drawer"
|
|
1525
|
+
Drawer open <=> showDrawer, side: "right"
|
|
1526
|
+
.drawer-panel
|
|
1527
|
+
h2 "Settings"
|
|
1528
|
+
p "This is a drawer panel that slides in from the right."
|
|
1529
|
+
p "Press Escape or click outside to close."
|
|
1530
|
+
button @click: (=> showDrawer = false)
|
|
1531
|
+
"Close"
|
|
1532
|
+
.api
|
|
1533
|
+
dl
|
|
1534
|
+
dt "Props"
|
|
1535
|
+
dd
|
|
1536
|
+
code "@open"
|
|
1537
|
+
code "@side"
|
|
1538
|
+
code "@dismissable"
|
|
1539
|
+
dt "Events"
|
|
1540
|
+
dd
|
|
1541
|
+
code "close"
|
|
1542
|
+
dt "Keyboard"
|
|
1543
|
+
dd
|
|
1544
|
+
kbd "Esc"
|
|
1545
|
+
kbd "Tab"
|
|
1546
|
+
dt "Data"
|
|
1547
|
+
dd
|
|
1548
|
+
code "$open"
|
|
1549
|
+
code "$side"
|
|
1550
|
+
|
|
1551
|
+
# ================================================================
|
|
1552
|
+
# POPOVER
|
|
1553
|
+
# ================================================================
|
|
1554
|
+
.section id: "popover"
|
|
1555
|
+
.section-title "Popover"
|
|
1556
|
+
span.badge @click: (=> @_viewSource('popover')), "143 lines ❐"
|
|
1557
|
+
.section-nav
|
|
1558
|
+
a @click: (=> @_navSection('popover', 'prev')), "‹"
|
|
1559
|
+
a @click: (=> @_navSection('popover', 'home')), "⌂"
|
|
1560
|
+
a @click: (=> @_navSection('popover', 'next')), "›"
|
|
1561
|
+
.section-desc "Anchored floating content with flip/shift positioning."
|
|
1562
|
+
.demo-row
|
|
1563
|
+
.demo-label "Click to toggle"
|
|
1564
|
+
Popover placement: "bottom-start"
|
|
1565
|
+
button.demo-btn data-trigger: true
|
|
1566
|
+
"Open Popover"
|
|
1567
|
+
div.floating-panel data-content: true
|
|
1568
|
+
p.floating-title "Popover content"
|
|
1569
|
+
p.floating-desc "Click outside or press Escape to close."
|
|
1570
|
+
.api
|
|
1571
|
+
dl
|
|
1572
|
+
dt "Props"
|
|
1573
|
+
dd
|
|
1574
|
+
code "@placement"
|
|
1575
|
+
code "@offset"
|
|
1576
|
+
code "@disabled"
|
|
1577
|
+
code "@openOnHover"
|
|
1578
|
+
code "@hoverDelay"
|
|
1579
|
+
code "@hoverCloseDelay"
|
|
1580
|
+
dt "Keyboard"
|
|
1581
|
+
dd
|
|
1582
|
+
kbd "Esc"
|
|
1583
|
+
kbd "Enter"
|
|
1584
|
+
kbd "Space"
|
|
1585
|
+
kbd "↓"
|
|
1586
|
+
dt "Data"
|
|
1587
|
+
dd
|
|
1588
|
+
code "$open"
|
|
1589
|
+
code "$placement"
|
|
1590
|
+
|
|
1591
|
+
# ================================================================
|
|
1592
|
+
# TOOLTIP
|
|
1593
|
+
# ================================================================
|
|
1594
|
+
.section id: "tooltip"
|
|
1595
|
+
.section-title "Tooltip"
|
|
1596
|
+
span.badge @click: (=> @_viewSource('tooltip')), "115 lines ❐"
|
|
1597
|
+
.section-nav
|
|
1598
|
+
a @click: (=> @_navSection('tooltip', 'prev')), "‹"
|
|
1599
|
+
a @click: (=> @_navSection('tooltip', 'home')), "⌂"
|
|
1600
|
+
a @click: (=> @_navSection('tooltip', 'next')), "›"
|
|
1601
|
+
.section-desc "Hover/focus tooltip with delay and positioning."
|
|
1602
|
+
.demo-row
|
|
1603
|
+
.demo-label "Hover the buttons"
|
|
1604
|
+
. style: "display:flex;gap:12px"
|
|
1605
|
+
Tooltip text: "Save your changes", placement: "top"
|
|
1606
|
+
button.demo-btn
|
|
1607
|
+
"Save (top)"
|
|
1608
|
+
Tooltip text: "Delete this item", placement: "bottom"
|
|
1609
|
+
button.demo-btn
|
|
1610
|
+
"Delete (bottom)"
|
|
1611
|
+
.api
|
|
1612
|
+
dl
|
|
1613
|
+
dt "Props"
|
|
1614
|
+
dd
|
|
1615
|
+
code "@text"
|
|
1616
|
+
code "@placement"
|
|
1617
|
+
code "@delay"
|
|
1618
|
+
code "@offset"
|
|
1619
|
+
code "@hoverable"
|
|
1620
|
+
dt "Data"
|
|
1621
|
+
dd
|
|
1622
|
+
code "$open"
|
|
1623
|
+
code "$entering"
|
|
1624
|
+
code "$exiting"
|
|
1625
|
+
code "$placement"
|
|
1626
|
+
|
|
1627
|
+
# ================================================================
|
|
1628
|
+
# PREVIEW CARD
|
|
1629
|
+
# ================================================================
|
|
1630
|
+
.section id: "preview-card"
|
|
1631
|
+
.section-title "PreviewCard"
|
|
1632
|
+
span.badge @click: (=> @_viewSource('preview-card')), "73 lines ❐"
|
|
1633
|
+
.section-nav
|
|
1634
|
+
a @click: (=> @_navSection('preview-card', 'prev')), "‹"
|
|
1635
|
+
a @click: (=> @_navSection('preview-card', 'home')), "⌂"
|
|
1636
|
+
a @click: (=> @_navSection('preview-card', 'next')), "›"
|
|
1637
|
+
.section-desc "Hover or focus the link to see a preview card."
|
|
1638
|
+
.demo-row
|
|
1639
|
+
PreviewCard delay: 300
|
|
1640
|
+
a $trigger: true, href: "#preview-card", @click: ((e) => e.preventDefault())
|
|
1641
|
+
"Hover me for preview"
|
|
1642
|
+
div $content: true, hidden: true
|
|
1643
|
+
.floating-panel style: "width:240px"
|
|
1644
|
+
p.floating-title "Preview Card"
|
|
1645
|
+
p.floating-desc "This content appears on hover or focus. Useful for link previews."
|
|
1646
|
+
.api
|
|
1647
|
+
dl
|
|
1648
|
+
dt "Props"
|
|
1649
|
+
dd
|
|
1650
|
+
code "@delay"
|
|
1651
|
+
code "@closeDelay"
|
|
1652
|
+
dt "Data"
|
|
1653
|
+
dd
|
|
1654
|
+
code "$open"
|
|
1655
|
+
|
|
1656
|
+
# ================================================================
|
|
1657
|
+
# TOAST
|
|
1658
|
+
# ================================================================
|
|
1659
|
+
.section id: "toast"
|
|
1660
|
+
.section-title "Toast"
|
|
1661
|
+
span.badge @click: (=> @_viewSource('toast')), "88 lines ❐"
|
|
1662
|
+
.section-nav
|
|
1663
|
+
a @click: (=> @_navSection('toast', 'prev')), "‹"
|
|
1664
|
+
a @click: (=> @_navSection('toast', 'home')), "⌂"
|
|
1665
|
+
a @click: (=> @_navSection('toast', 'next')), "›"
|
|
1666
|
+
.section-desc "Auto-dismiss notification with ARIA live region."
|
|
1667
|
+
.demo-row
|
|
1668
|
+
.demo-label "Click to show toasts"
|
|
1669
|
+
button.demo-btn @click: (=> toasts = [...toasts, { message: 'Hello!', type: 'info' }])
|
|
1670
|
+
"Info Toast"
|
|
1671
|
+
button.demo-btn style: "margin-left:8px", @click: (=> toasts = [...toasts, { message: 'Saved!', type: 'success' }])
|
|
1672
|
+
"Success Toast"
|
|
1673
|
+
.toast-wrap
|
|
1674
|
+
ToastViewport toasts <=> toasts
|
|
1675
|
+
.api
|
|
1676
|
+
dl
|
|
1677
|
+
dt "Props"
|
|
1678
|
+
dd
|
|
1679
|
+
code "@toasts"
|
|
1680
|
+
code "@toast"
|
|
1681
|
+
code "@placement"
|
|
1682
|
+
dt "Events"
|
|
1683
|
+
dd
|
|
1684
|
+
code "dismiss"
|
|
1685
|
+
dt "Data"
|
|
1686
|
+
dd
|
|
1687
|
+
code "$placement"
|
|
1688
|
+
code "$type"
|
|
1689
|
+
code "$leaving"
|
|
1690
|
+
|
|
1691
|
+
# ================================================================
|
|
1692
|
+
# BUTTON
|
|
1693
|
+
# ================================================================
|
|
1694
|
+
.section id: "button"
|
|
1695
|
+
.section-title "Button"
|
|
1696
|
+
span.badge @click: (=> @_viewSource('button')), "23 lines ❐"
|
|
1697
|
+
.section-nav
|
|
1698
|
+
a @click: (=> @_navSection('button', 'prev')), "‹"
|
|
1699
|
+
a @click: (=> @_navSection('button', 'home')), "⌂"
|
|
1700
|
+
a @click: (=> @_navSection('button', 'next')), "›"
|
|
1701
|
+
.section-desc "Headless button with disabled-but-focusable pattern."
|
|
1702
|
+
.demo-row.btn-demo
|
|
1703
|
+
.demo-label "Normal, primary, and disabled"
|
|
1704
|
+
.btn-row
|
|
1705
|
+
Button @press: (=> p "Pressed!")
|
|
1706
|
+
"Click Me"
|
|
1707
|
+
Button @press: (=> p "Saved!")
|
|
1708
|
+
"Save"
|
|
1709
|
+
Button disabled: true
|
|
1710
|
+
"Disabled"
|
|
1711
|
+
.api
|
|
1712
|
+
dl
|
|
1713
|
+
dt "Props"
|
|
1714
|
+
dd
|
|
1715
|
+
code "@disabled"
|
|
1716
|
+
dt "Events"
|
|
1717
|
+
dd
|
|
1718
|
+
code "press"
|
|
1719
|
+
dt "Data"
|
|
1720
|
+
dd
|
|
1721
|
+
code "$disabled"
|
|
1722
|
+
|
|
1723
|
+
# ================================================================
|
|
1724
|
+
# SEPARATOR
|
|
1725
|
+
# ================================================================
|
|
1726
|
+
.section id: "separator"
|
|
1727
|
+
.section-title "Separator"
|
|
1728
|
+
span.badge @click: (=> @_viewSource('separator')), "17 lines ❐"
|
|
1729
|
+
.section-nav
|
|
1730
|
+
a @click: (=> @_navSection('separator', 'prev')), "‹"
|
|
1731
|
+
a @click: (=> @_navSection('separator', 'home')), "⌂"
|
|
1732
|
+
a @click: (=> @_navSection('separator', 'next')), "›"
|
|
1733
|
+
.section-desc "Decorative or semantic divider between sections."
|
|
1734
|
+
.demo-row
|
|
1735
|
+
.demo-label "Horizontal (default)"
|
|
1736
|
+
div style: "width:100%"
|
|
1737
|
+
p "Above the separator"
|
|
1738
|
+
Separator
|
|
1739
|
+
p "Below the separator"
|
|
1740
|
+
.demo-row
|
|
1741
|
+
.demo-label "Vertical"
|
|
1742
|
+
div style: "display:flex;align-items:center;gap:12px;height:32px"
|
|
1743
|
+
span "Left"
|
|
1744
|
+
Separator orientation: "vertical"
|
|
1745
|
+
span "Right"
|
|
1746
|
+
.api
|
|
1747
|
+
dl
|
|
1748
|
+
dt "Props"
|
|
1749
|
+
dd
|
|
1750
|
+
code "@orientation"
|
|
1751
|
+
code "@decorative"
|
|
1752
|
+
dt "Data"
|
|
1753
|
+
dd
|
|
1754
|
+
code "$orientation"
|
|
1755
|
+
|
|
1756
|
+
# ================================================================
|
|
1757
|
+
# PROGRESS
|
|
1758
|
+
# ================================================================
|
|
1759
|
+
.section id: "progress"
|
|
1760
|
+
.section-title "Progress"
|
|
1761
|
+
span.badge @click: (=> @_viewSource('progress')), "25 lines ❐"
|
|
1762
|
+
.section-nav
|
|
1763
|
+
a @click: (=> @_navSection('progress', 'prev')), "‹"
|
|
1764
|
+
a @click: (=> @_navSection('progress', 'home')), "⌂"
|
|
1765
|
+
a @click: (=> @_navSection('progress', 'next')), "›"
|
|
1766
|
+
.section-desc "Progress bar with value exposed as CSS custom property."
|
|
1767
|
+
.demo-row
|
|
1768
|
+
.demo-label "65% complete"
|
|
1769
|
+
Progress value: progressVal
|
|
1770
|
+
div.progress-track
|
|
1771
|
+
div.progress-fill style: "width: var(--progress-percent)"
|
|
1772
|
+
.api
|
|
1773
|
+
dl
|
|
1774
|
+
dt "Props"
|
|
1775
|
+
dd
|
|
1776
|
+
code "@value"
|
|
1777
|
+
code "@max"
|
|
1778
|
+
code "@label"
|
|
1779
|
+
dt "Data"
|
|
1780
|
+
dd
|
|
1781
|
+
code "$complete"
|
|
1782
|
+
|
|
1783
|
+
# ================================================================
|
|
1784
|
+
# METER
|
|
1785
|
+
# ================================================================
|
|
1786
|
+
.section id: "meter"
|
|
1787
|
+
.section-title "Meter"
|
|
1788
|
+
span.badge @click: (=> @_viewSource('meter')), "36 lines ❐"
|
|
1789
|
+
.section-nav
|
|
1790
|
+
a @click: (=> @_navSection('meter', 'prev')), "‹"
|
|
1791
|
+
a @click: (=> @_navSection('meter', 'home')), "⌂"
|
|
1792
|
+
a @click: (=> @_navSection('meter', 'next')), "›"
|
|
1793
|
+
.section-desc "Gauge for known-range measurements with low/high/optimum thresholds."
|
|
1794
|
+
.demo-row
|
|
1795
|
+
.demo-label "Score: 72/100"
|
|
1796
|
+
Meter value: meterVal, min: 0, max: 100, low: 30, high: 80, optimum: 60
|
|
1797
|
+
.api
|
|
1798
|
+
dl
|
|
1799
|
+
dt "Props"
|
|
1800
|
+
dd
|
|
1801
|
+
code "@value"
|
|
1802
|
+
code "@min"
|
|
1803
|
+
code "@max"
|
|
1804
|
+
code "@low"
|
|
1805
|
+
code "@high"
|
|
1806
|
+
code "@optimum"
|
|
1807
|
+
code "@label"
|
|
1808
|
+
dt "Data"
|
|
1809
|
+
dd
|
|
1810
|
+
code "$level"
|
|
1811
|
+
|
|
1812
|
+
# ================================================================
|
|
1813
|
+
# AVATAR
|
|
1814
|
+
# ================================================================
|
|
1815
|
+
.section id: "avatar"
|
|
1816
|
+
.section-title "Avatar"
|
|
1817
|
+
span.badge @click: (=> @_viewSource('avatar')), "37 lines ❐"
|
|
1818
|
+
.section-nav
|
|
1819
|
+
a @click: (=> @_navSection('avatar', 'prev')), "‹"
|
|
1820
|
+
a @click: (=> @_navSection('avatar', 'home')), "⌂"
|
|
1821
|
+
a @click: (=> @_navSection('avatar', 'next')), "›"
|
|
1822
|
+
.section-desc "Image with fallback to initials or placeholder."
|
|
1823
|
+
.demo-row
|
|
1824
|
+
.demo-label "With image"
|
|
1825
|
+
Avatar src: "https://i.pravatar.cc/80?u=linda", alt: "Alice Chen"
|
|
1826
|
+
.demo-row
|
|
1827
|
+
.demo-label "Initials fallback"
|
|
1828
|
+
Avatar alt: "Bob Park", fallback: "BP"
|
|
1829
|
+
.demo-row
|
|
1830
|
+
.demo-label "Placeholder"
|
|
1831
|
+
Avatar
|
|
1832
|
+
.api
|
|
1833
|
+
dl
|
|
1834
|
+
dt "Props"
|
|
1835
|
+
dd
|
|
1836
|
+
code "@src"
|
|
1837
|
+
code "@alt"
|
|
1838
|
+
code "@fallback"
|
|
1839
|
+
dt "Data"
|
|
1840
|
+
dd
|
|
1841
|
+
code "$status"
|
|
1842
|
+
code "$initials"
|
|
1843
|
+
code "$placeholder"
|
|
1844
|
+
|
|
1845
|
+
# ================================================================
|
|
1846
|
+
# SCROLL AREA
|
|
1847
|
+
# ================================================================
|
|
1848
|
+
.section id: "scroll-area"
|
|
1849
|
+
.section-title "ScrollArea"
|
|
1850
|
+
span.badge @click: (=> @_viewSource('scroll-area')), "145 lines ❐"
|
|
1851
|
+
.section-nav
|
|
1852
|
+
a @click: (=> @_navSection('scroll-area', 'prev')), "‹"
|
|
1853
|
+
a @click: (=> @_navSection('scroll-area', 'home')), "⌂"
|
|
1854
|
+
a @click: (=> @_navSection('scroll-area', 'next')), "›"
|
|
1855
|
+
.section-desc "Custom scrollbar with draggable thumb and auto-hide."
|
|
1856
|
+
.demo-row
|
|
1857
|
+
.demo-label "Scrollable content"
|
|
1858
|
+
.scroll-demo
|
|
1859
|
+
ScrollArea
|
|
1860
|
+
div style: "padding: 4px 12px"
|
|
1861
|
+
p "Item 1 — Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
|
1862
|
+
p "Item 2 — Sed do eiusmod tempor incididunt ut labore et dolore."
|
|
1863
|
+
p "Item 3 — Ut enim ad minim veniam, quis nostrud exercitation."
|
|
1864
|
+
p "Item 4 — Duis aute irure dolor in reprehenderit in voluptate."
|
|
1865
|
+
p "Item 5 — Excepteur sint occaecat cupidatat non proident."
|
|
1866
|
+
p "Item 6 — Sunt in culpa qui officia deserunt mollit anim id."
|
|
1867
|
+
p "Item 7 — Lorem ipsum dolor sit amet, consectetur adipiscing."
|
|
1868
|
+
p "Item 8 — Sed do eiusmod tempor incididunt ut labore et dolore."
|
|
1869
|
+
p "Item 9 — Ut enim ad minim veniam, quis nostrud exercitation."
|
|
1870
|
+
p "Item 10 — Duis aute irure dolor in reprehenderit in voluptate."
|
|
1871
|
+
p "Item 11 — Excepteur sint occaecat cupidatat non proident."
|
|
1872
|
+
p "Item 12 — Sunt in culpa qui officia deserunt mollit anim id."
|
|
1873
|
+
p "Item 13 — Lorem ipsum dolor sit amet, consectetur adipiscing."
|
|
1874
|
+
p "Item 14 — Sed do eiusmod tempor incididunt ut labore et dolore."
|
|
1875
|
+
p "Item 15 — Ut enim ad minim veniam, quis nostrud exercitation."
|
|
1876
|
+
.api
|
|
1877
|
+
dl
|
|
1878
|
+
dt "Props"
|
|
1879
|
+
dd
|
|
1880
|
+
code "@orientation"
|
|
1881
|
+
dt "Data"
|
|
1882
|
+
dd
|
|
1883
|
+
code "$orientation"
|
|
1884
|
+
code "$hovering"
|
|
1885
|
+
code "$scrolling"
|
|
1886
|
+
code "$dragging"
|
|
1887
|
+
code "$viewport"
|
|
1888
|
+
code "$scrollbar"
|
|
1889
|
+
code "$thumb"
|
|
1890
|
+
|
|
1891
|
+
# ================================================================
|
|
1892
|
+
# FIELD + FIELDSET
|
|
1893
|
+
# ================================================================
|
|
1894
|
+
.section id: "field"
|
|
1895
|
+
.section-title "Field + Fieldset"
|
|
1896
|
+
span.badge @click: (=> @_viewSource('field')), "75 lines ❐"
|
|
1897
|
+
.section-nav
|
|
1898
|
+
a @click: (=> @_navSection('field', 'prev')), "‹"
|
|
1899
|
+
a @click: (=> @_navSection('field', 'home')), "⌂"
|
|
1900
|
+
a @click: (=> @_navSection('field', 'next')), "›"
|
|
1901
|
+
.section-desc "Form field wrapper with label, description, and error."
|
|
1902
|
+
.demo-row
|
|
1903
|
+
Field label: "Email", description: "We'll never share your email.", required: true, error: fieldError
|
|
1904
|
+
input type: "email", placeholder: "you@example.com", value: fieldEmail
|
|
1905
|
+
@input: (e) =>
|
|
1906
|
+
fieldEmail = e.target.value
|
|
1907
|
+
fieldError = if fieldEmail and not fieldEmail.includes('@') then 'Invalid email address' else ''
|
|
1908
|
+
.demo-row
|
|
1909
|
+
Fieldset legend: "Account Info"
|
|
1910
|
+
Field label: "Username"
|
|
1911
|
+
input type: "text", placeholder: "username"
|
|
1912
|
+
Field label: "Password"
|
|
1913
|
+
input type: "password", placeholder: "password"
|
|
1914
|
+
.api
|
|
1915
|
+
dl
|
|
1916
|
+
dt "Field"
|
|
1917
|
+
dd
|
|
1918
|
+
code "@label"
|
|
1919
|
+
code "@description"
|
|
1920
|
+
code "@error"
|
|
1921
|
+
code "@disabled"
|
|
1922
|
+
code "@required"
|
|
1923
|
+
dt "Fieldset"
|
|
1924
|
+
dd
|
|
1925
|
+
code "@legend"
|
|
1926
|
+
code "@disabled"
|
|
1927
|
+
dt "Data"
|
|
1928
|
+
dd
|
|
1929
|
+
code "$disabled"
|
|
1930
|
+
code "$invalid"
|
|
1931
|
+
code "$label"
|
|
1932
|
+
code "$required"
|
|
1933
|
+
code "$description"
|
|
1934
|
+
code "$error"
|
|
1935
|
+
code "$legend"
|
|
1936
|
+
|
|
1937
|
+
# ================================================================
|
|
1938
|
+
# FORM
|
|
1939
|
+
# ================================================================
|
|
1940
|
+
.section id: "form"
|
|
1941
|
+
.section-title "Form"
|
|
1942
|
+
span.badge @click: (=> @_viewSource('form')), "39 lines ❐"
|
|
1943
|
+
.section-nav
|
|
1944
|
+
a @click: (=> @_navSection('form', 'prev')), "‹"
|
|
1945
|
+
a @click: (=> @_navSection('form', 'home')), "⌂"
|
|
1946
|
+
a @click: (=> @_navSection('form', 'next')), "›"
|
|
1947
|
+
.section-desc "Form wrapper with submit handling and validation state."
|
|
1948
|
+
.demo-row
|
|
1949
|
+
Form
|
|
1950
|
+
Field label: "Name"
|
|
1951
|
+
input type: "text", placeholder: "Your name"
|
|
1952
|
+
Field label: "Email"
|
|
1953
|
+
input type: "email", placeholder: "you@example.com"
|
|
1954
|
+
button type: "submit"
|
|
1955
|
+
"Submit"
|
|
1956
|
+
.api
|
|
1957
|
+
dl
|
|
1958
|
+
dt "Props"
|
|
1959
|
+
dd
|
|
1960
|
+
code "@disabled"
|
|
1961
|
+
dt "Events"
|
|
1962
|
+
dd
|
|
1963
|
+
code "submit"
|
|
1964
|
+
dt "Data"
|
|
1965
|
+
dd
|
|
1966
|
+
code "$disabled"
|
|
1967
|
+
code "$submitting"
|
|
1968
|
+
code "$submitted"
|
|
1969
|
+
|
|
1970
|
+
# ================================================================
|
|
1971
|
+
# GRID
|
|
1972
|
+
# ================================================================
|
|
1973
|
+
.section id: "grid"
|
|
1974
|
+
.section-title "Grid"
|
|
1975
|
+
span.badge @click: (=> @_viewSource('grid')), "901 lines ❐"
|
|
1976
|
+
.section-nav
|
|
1977
|
+
a @click: (=> @_navSection('grid', 'prev')), "‹"
|
|
1978
|
+
a @click: (=> @_navSection('grid', 'home')), "⌂"
|
|
1979
|
+
a @click: (=> @_navSection('grid', 'next')), "›"
|
|
1980
|
+
.section-desc "Virtual-scrolling data grid. Click to select, arrows to navigate, double-click to edit."
|
|
1981
|
+
.demo-row
|
|
1982
|
+
. style: "display:flex;justify-content:space-between;align-items:baseline;margin-bottom:8px"
|
|
1983
|
+
.demo-label style: "margin-bottom:0", "#{gridData.length} rows, #{gridColumns.length} columns"
|
|
1984
|
+
span.grid-cell-ref gridCellRef
|
|
1985
|
+
Grid data: gridData, columns: gridColumns, striped: true
|
|
1986
|
+
. style: "margin-top:12px;display:flex;gap:8px;align-items:center"
|
|
1987
|
+
button.demo-btn @click: (=> @_loadGridRows(1000))
|
|
1988
|
+
"1K rows"
|
|
1989
|
+
button.demo-btn @click: (=> @_loadGridRows(10000))
|
|
1990
|
+
"10K rows"
|
|
1991
|
+
button.demo-btn @click: (=> @_loadGridRows(100000))
|
|
1992
|
+
"100K rows"
|
|
1993
|
+
.api
|
|
1994
|
+
dl
|
|
1995
|
+
dt "Props"
|
|
1996
|
+
dd
|
|
1997
|
+
code "@data"
|
|
1998
|
+
code "@columns"
|
|
1999
|
+
code "@rowHeight"
|
|
2000
|
+
code "@overscan"
|
|
2001
|
+
code "@striped"
|
|
2002
|
+
code "@beforeEdit"
|
|
2003
|
+
code "@afterEdit"
|
|
2004
|
+
dt "Keyboard"
|
|
2005
|
+
dd
|
|
2006
|
+
kbd "Arrows"
|
|
2007
|
+
kbd "Tab"
|
|
2008
|
+
kbd "Enter"
|
|
2009
|
+
kbd "F2"
|
|
2010
|
+
kbd "Esc"
|
|
2011
|
+
kbd "Home"
|
|
2012
|
+
kbd "End"
|
|
2013
|
+
kbd "PgUp"
|
|
2014
|
+
kbd "PgDn"
|
|
2015
|
+
kbd "Ctrl+A"
|
|
2016
|
+
kbd "Ctrl+C"
|
|
2017
|
+
kbd "Ctrl+V"
|
|
2018
|
+
kbd "Ctrl+X"
|
|
2019
|
+
dt "Data"
|
|
2020
|
+
dd
|
|
2021
|
+
code "$editing"
|
|
2022
|
+
code "$selecting"
|
|
2023
|
+
code "$sorted"
|
|
2024
|
+
|
|
2025
|
+
# ================================================================
|
|
2026
|
+
# ACCORDION
|
|
2027
|
+
# ================================================================
|
|
2028
|
+
.section id: "accordion"
|
|
2029
|
+
.section-title "Accordion"
|
|
2030
|
+
span.badge @click: (=> @_viewSource('accordion')), "113 lines ❐"
|
|
2031
|
+
.section-nav
|
|
2032
|
+
a @click: (=> @_navSection('accordion', 'prev')), "‹"
|
|
2033
|
+
a @click: (=> @_navSection('accordion', 'home')), "⌂"
|
|
2034
|
+
a @click: (=> @_navSection('accordion', 'next')), "›"
|
|
2035
|
+
.section-desc "Expand/collapse sections, single or multiple mode."
|
|
2036
|
+
.demo-row
|
|
2037
|
+
.demo-label "Single mode"
|
|
2038
|
+
Accordion
|
|
2039
|
+
div data-item: "a"
|
|
2040
|
+
button.accordion-trigger data-trigger: true
|
|
2041
|
+
"Section A"
|
|
2042
|
+
div.accordion-content data-content: true
|
|
2043
|
+
p "Content for section A."
|
|
2044
|
+
div data-item: "b"
|
|
2045
|
+
button.accordion-trigger data-trigger: true
|
|
2046
|
+
"Section B"
|
|
2047
|
+
div.accordion-content data-content: true
|
|
2048
|
+
p "Content for section B."
|
|
2049
|
+
div data-item: "c"
|
|
2050
|
+
button.accordion-trigger data-trigger: true
|
|
2051
|
+
"Section C"
|
|
2052
|
+
div.accordion-content data-content: true
|
|
2053
|
+
p "Content for section C."
|
|
2054
|
+
.api
|
|
2055
|
+
dl
|
|
2056
|
+
dt "Props"
|
|
2057
|
+
dd
|
|
2058
|
+
code "@multiple"
|
|
2059
|
+
dt "Events"
|
|
2060
|
+
dd
|
|
2061
|
+
code "change"
|
|
2062
|
+
dt "Keyboard"
|
|
2063
|
+
dd
|
|
2064
|
+
kbd "Enter"
|
|
2065
|
+
kbd "Space"
|
|
2066
|
+
kbd "↕"
|
|
2067
|
+
kbd "Home"
|
|
2068
|
+
kbd "End"
|
|
2069
|
+
dt "Data"
|
|
2070
|
+
dd
|
|
2071
|
+
code "$item"
|
|
2072
|
+
code "$trigger"
|
|
2073
|
+
code "$content"
|
|
2074
|
+
code "$open"
|
|
2075
|
+
code "$disabled"
|
|
2076
|
+
|
|
2077
|
+
# ================================================================
|
|
2078
|
+
# SOURCE CODE VIEWER
|
|
2079
|
+
# ================================================================
|
|
2080
|
+
if sourceCode
|
|
2081
|
+
.source-overlay @click: @_closeSource
|
|
2082
|
+
.source-modal @click: (e => e.stopPropagation())
|
|
2083
|
+
.source-header
|
|
2084
|
+
.source-title
|
|
2085
|
+
span.source-name sourceName
|
|
2086
|
+
span.source-badge "#{sourceLines} lines"
|
|
2087
|
+
button.source-close @click: @_closeSource
|
|
2088
|
+
"×"
|
|
2089
|
+
.source-body
|
|
2090
|
+
pre.source-pre
|
|
2091
|
+
span.source-gutter
|
|
2092
|
+
code.source-code
|
|
2093
|
+
|
|
2094
|
+
</script>
|
|
2095
|
+
|
|
2096
|
+
</body>
|
|
2097
|
+
</html>
|