@rip-lang/ui 0.3.67 → 0.4.1
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/AGENTS.md +93 -0
- package/README.md +22 -625
- package/browser/AGENTS.md +213 -0
- package/browser/CONTRIBUTING.md +375 -0
- package/browser/README.md +11 -0
- package/browser/TESTING.md +59 -0
- package/browser/browser.rip +56 -0
- package/{components → browser/components}/accordion.rip +1 -1
- package/{components → browser/components}/alert-dialog.rip +6 -3
- package/{components → browser/components}/autocomplete.rip +27 -21
- package/{components → browser/components}/avatar.rip +3 -3
- package/{components → browser/components}/badge.rip +1 -1
- package/{components → browser/components}/breadcrumb.rip +2 -2
- package/{components → browser/components}/button-group.rip +3 -3
- package/{components → browser/components}/button.rip +2 -2
- package/{components → browser/components}/card.rip +1 -1
- package/{components → browser/components}/carousel.rip +5 -5
- package/{components → browser/components}/checkbox-group.rip +40 -11
- package/{components → browser/components}/checkbox.rip +4 -4
- package/{components → browser/components}/collapsible.rip +2 -2
- package/{components → browser/components}/combobox.rip +36 -23
- package/{components → browser/components}/context-menu.rip +1 -1
- package/{components → browser/components}/date-picker.rip +5 -5
- package/{components → browser/components}/dialog.rip +8 -4
- package/{components → browser/components}/drawer.rip +8 -4
- package/{components → browser/components}/editable-value.rip +7 -1
- package/{components → browser/components}/field.rip +5 -5
- package/{components → browser/components}/fieldset.rip +2 -2
- package/{components → browser/components}/form.rip +1 -1
- package/{components → browser/components}/grid.rip +8 -8
- package/{components → browser/components}/input-group.rip +1 -1
- package/{components → browser/components}/input.rip +6 -6
- package/{components → browser/components}/label.rip +2 -2
- package/{components → browser/components}/menu.rip +17 -10
- package/{components → browser/components}/menubar.rip +1 -1
- package/{components → browser/components}/meter.rip +7 -7
- package/{components → browser/components}/multi-select.rip +76 -33
- package/{components → browser/components}/native-select.rip +3 -3
- package/{components → browser/components}/nav-menu.rip +3 -3
- package/{components → browser/components}/number-field.rip +11 -11
- package/{components → browser/components}/otp-field.rip +4 -4
- package/{components → browser/components}/pagination.rip +4 -4
- package/{components → browser/components}/popover.rip +11 -24
- package/{components → browser/components}/preview-card.rip +7 -11
- package/{components → browser/components}/progress.rip +3 -3
- package/{components → browser/components}/radio-group.rip +4 -4
- package/{components → browser/components}/resizable.rip +3 -3
- package/{components → browser/components}/scroll-area.rip +1 -1
- package/{components → browser/components}/select.rip +55 -27
- package/{components → browser/components}/separator.rip +2 -2
- package/{components → browser/components}/skeleton.rip +4 -4
- package/{components → browser/components}/slider.rip +15 -10
- package/{components → browser/components}/spinner.rip +2 -2
- package/{components → browser/components}/table.rip +2 -2
- package/{components → browser/components}/tabs.rip +12 -7
- package/{components → browser/components}/textarea.rip +8 -8
- package/{components → browser/components}/toast.rip +3 -3
- package/{components → browser/components}/toggle-group.rip +42 -11
- package/{components → browser/components}/toggle.rip +2 -2
- package/{components → browser/components}/toolbar.rip +2 -2
- package/{components → browser/components}/tooltip.rip +19 -23
- package/browser/hljs-rip.js +209 -0
- package/browser/playwright.config.mjs +31 -0
- package/browser/tests/overlays.js +349 -0
- package/email/AGENTS.md +16 -0
- package/email/README.md +55 -0
- package/email/benchmarks/benchmark.rip +94 -0
- package/email/benchmarks/samples.rip +104 -0
- package/email/compat.rip +129 -0
- package/email/components.rip +371 -0
- package/email/dom.rip +330 -0
- package/email/email.rip +10 -0
- package/email/render.rip +82 -0
- package/package.json +29 -39
- package/shared/README.md +3 -0
- package/shared/styles.rip +17 -0
- package/tailwind/AGENTS.md +3 -0
- package/tailwind/README.md +27 -0
- package/tailwind/engine.js +107 -0
- package/tailwind/inline.js +215 -0
- package/tailwind/serve.js +6 -0
- package/tailwind/tailwind.rip +13 -0
- package/ui.rip +3 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Browser domain barrel for @rip-lang/ui/browser
|
|
2
|
+
|
|
3
|
+
export * from './components/accordion.rip'
|
|
4
|
+
export * from './components/alert-dialog.rip'
|
|
5
|
+
export * from './components/autocomplete.rip'
|
|
6
|
+
export * from './components/avatar.rip'
|
|
7
|
+
export * from './components/badge.rip'
|
|
8
|
+
export * from './components/breadcrumb.rip'
|
|
9
|
+
export * from './components/button-group.rip'
|
|
10
|
+
export * from './components/button.rip'
|
|
11
|
+
export * from './components/card.rip'
|
|
12
|
+
export * from './components/carousel.rip'
|
|
13
|
+
export * from './components/checkbox-group.rip'
|
|
14
|
+
export * from './components/checkbox.rip'
|
|
15
|
+
export * from './components/collapsible.rip'
|
|
16
|
+
export * from './components/combobox.rip'
|
|
17
|
+
export * from './components/context-menu.rip'
|
|
18
|
+
export * from './components/date-picker.rip'
|
|
19
|
+
export * from './components/dialog.rip'
|
|
20
|
+
export * from './components/drawer.rip'
|
|
21
|
+
export * from './components/editable-value.rip'
|
|
22
|
+
export * from './components/field.rip'
|
|
23
|
+
export * from './components/fieldset.rip'
|
|
24
|
+
export * from './components/form.rip'
|
|
25
|
+
export * from './components/grid.rip'
|
|
26
|
+
export * from './components/input-group.rip'
|
|
27
|
+
export * from './components/input.rip'
|
|
28
|
+
export * from './components/label.rip'
|
|
29
|
+
export * from './components/menu.rip'
|
|
30
|
+
export * from './components/menubar.rip'
|
|
31
|
+
export * from './components/meter.rip'
|
|
32
|
+
export * from './components/multi-select.rip'
|
|
33
|
+
export * from './components/native-select.rip'
|
|
34
|
+
export * from './components/nav-menu.rip'
|
|
35
|
+
export * from './components/number-field.rip'
|
|
36
|
+
export * from './components/otp-field.rip'
|
|
37
|
+
export * from './components/pagination.rip'
|
|
38
|
+
export * from './components/popover.rip'
|
|
39
|
+
export * from './components/preview-card.rip'
|
|
40
|
+
export * from './components/progress.rip'
|
|
41
|
+
export * from './components/radio-group.rip'
|
|
42
|
+
export * from './components/resizable.rip'
|
|
43
|
+
export * from './components/scroll-area.rip'
|
|
44
|
+
export * from './components/select.rip'
|
|
45
|
+
export * from './components/separator.rip'
|
|
46
|
+
export * from './components/skeleton.rip'
|
|
47
|
+
export * from './components/slider.rip'
|
|
48
|
+
export * from './components/spinner.rip'
|
|
49
|
+
export * from './components/table.rip'
|
|
50
|
+
export * from './components/tabs.rip'
|
|
51
|
+
export * from './components/textarea.rip'
|
|
52
|
+
export * from './components/toast.rip'
|
|
53
|
+
export * from './components/toggle-group.rip'
|
|
54
|
+
export * from './components/toggle.rip'
|
|
55
|
+
export * from './components/toolbar.rip'
|
|
56
|
+
export * from './components/tooltip.rip'
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
# button @click: handleDelete, "Delete"
|
|
14
14
|
|
|
15
15
|
export AlertDialog = component
|
|
16
|
-
@open := false
|
|
17
|
-
@initialFocus := null
|
|
16
|
+
@open:: boolean := false
|
|
17
|
+
@initialFocus:: any := null
|
|
18
18
|
|
|
19
19
|
_prevFocus = null
|
|
20
20
|
_id =! "adlg-#{Math.random().toString(36).slice(2, 8)}"
|
|
@@ -49,5 +49,8 @@ export AlertDialog = component
|
|
|
49
49
|
@emit 'close'
|
|
50
50
|
|
|
51
51
|
render
|
|
52
|
-
dialog ref: "_dialog"
|
|
52
|
+
dialog ref: "_dialog"
|
|
53
|
+
hidden: not @open
|
|
54
|
+
aria-hidden: (@open ? undefined : "true")
|
|
55
|
+
$open: @open?!
|
|
53
56
|
slot
|
|
@@ -10,12 +10,14 @@
|
|
|
10
10
|
acCollator = new Intl.Collator(undefined, { sensitivity: 'base' })
|
|
11
11
|
|
|
12
12
|
export Autocomplete = component
|
|
13
|
-
@value :=
|
|
14
|
-
@items := []
|
|
15
|
-
@placeholder :=
|
|
16
|
-
@disabled := false
|
|
13
|
+
@value:: string := ""
|
|
14
|
+
@items:: any[] := []
|
|
15
|
+
@placeholder:: string := "Type to search..."
|
|
16
|
+
@disabled:: boolean := false
|
|
17
17
|
|
|
18
18
|
open := false
|
|
19
|
+
_ready := false
|
|
20
|
+
_popupGuard =! ARIA.popupGuard()
|
|
19
21
|
|
|
20
22
|
filteredItems ~=
|
|
21
23
|
q = @value.trim()
|
|
@@ -43,26 +45,20 @@ export Autocomplete = component
|
|
|
43
45
|
opts[idx]?.scrollIntoView({ block: 'nearest' })
|
|
44
46
|
|
|
45
47
|
openMenu: ->
|
|
48
|
+
return unless _popupGuard.canOpen()
|
|
46
49
|
open = true
|
|
47
50
|
@_hlIdx = -1
|
|
48
51
|
@_input?.focus()
|
|
49
52
|
|
|
50
|
-
close: ->
|
|
53
|
+
close: (restoreFocus = true, blockOpen = false) ->
|
|
51
54
|
open = false
|
|
52
55
|
@_hlIdx = -1
|
|
53
|
-
@_input?.
|
|
56
|
+
@_input?.removeAttribute 'aria-activedescendant'
|
|
57
|
+
_popupGuard.block() if blockOpen
|
|
58
|
+
@_input?.focus() if restoreFocus
|
|
54
59
|
|
|
55
60
|
_applyPlacement: ->
|
|
56
|
-
|
|
57
|
-
*>@_list.style =
|
|
58
|
-
position: 'fixed'
|
|
59
|
-
inset: 'auto'
|
|
60
|
-
margin: '0'
|
|
61
|
-
positionArea: 'bottom start'
|
|
62
|
-
positionTry: 'flip-block, flip-inline, flip-block flip-inline'
|
|
63
|
-
positionVisibility: 'anchors-visible'
|
|
64
|
-
marginTop: '2px'
|
|
65
|
-
minWidth: 'anchor-size(width)'
|
|
61
|
+
ARIA.position @_input, @_list, placement: 'bottom start', offset: 2, matchWidth: true
|
|
66
62
|
|
|
67
63
|
selectIndex: (idx) ->
|
|
68
64
|
item = filteredItems[idx]
|
|
@@ -71,7 +67,7 @@ export Autocomplete = component
|
|
|
71
67
|
@value = label
|
|
72
68
|
@_input?.value = label
|
|
73
69
|
@emit 'select', item
|
|
74
|
-
@close()
|
|
70
|
+
@close(true, true)
|
|
75
71
|
|
|
76
72
|
onInput: (e) ->
|
|
77
73
|
newVal = e.target.value
|
|
@@ -90,15 +86,17 @@ export Autocomplete = component
|
|
|
90
86
|
last: => if len then @_hlIdx = len - 1; @_updateHighlight()
|
|
91
87
|
select: => @selectIndex(@_hlIdx) if @_hlIdx >= 0
|
|
92
88
|
dismiss: => @close()
|
|
93
|
-
tab: => @close()
|
|
89
|
+
tab: => @close(false, true)
|
|
94
90
|
|
|
95
91
|
~>
|
|
92
|
+
return unless _ready
|
|
96
93
|
if @_list
|
|
97
|
-
@_list.setAttribute 'popover', 'auto'
|
|
98
94
|
@_applyPlacement()
|
|
99
95
|
ARIA.bindPopover open, (=> @_list), ((isOpen) => open = isOpen), (=> @_input)
|
|
96
|
+
ARIA.popupDismiss open, (=> @_list), (=> @close(false, true)), [=> @_input]
|
|
100
97
|
|
|
101
98
|
mounted: ->
|
|
99
|
+
_ready = true
|
|
102
100
|
@_hlIdx = -1
|
|
103
101
|
@_input.value = @value if @_input and @value
|
|
104
102
|
|
|
@@ -115,8 +113,16 @@ export Autocomplete = component
|
|
|
115
113
|
disabled: @disabled
|
|
116
114
|
placeholder: @placeholder
|
|
117
115
|
@input: @onInput
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
@keydown: @onKeydown
|
|
117
|
+
|
|
118
|
+
div ref: "_list"
|
|
119
|
+
id: _listId
|
|
120
|
+
role: "listbox"
|
|
121
|
+
popover: "auto"
|
|
122
|
+
hidden: not open
|
|
123
|
+
aria-hidden: (open ? undefined : "true")
|
|
124
|
+
$open: open?!
|
|
125
|
+
style: "position:fixed;margin:0;inset:auto"
|
|
120
126
|
for item, idx in filteredItems
|
|
121
127
|
div role: "option", tabindex: "-1"
|
|
122
128
|
@click: (=> @selectIndex(idx))
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
# Toggle pressed <=> isItalic, "Italic"
|
|
14
14
|
|
|
15
15
|
export ButtonGroup = component
|
|
16
|
-
@orientation :=
|
|
17
|
-
@disabled := false
|
|
18
|
-
@label :=
|
|
16
|
+
@orientation:: "horizontal" | "vertical" := "horizontal"
|
|
17
|
+
@disabled:: boolean := false
|
|
18
|
+
@label:: string := ""
|
|
19
19
|
|
|
20
20
|
render
|
|
21
21
|
div role: "group"
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
# div $slide: true, "Slide B"
|
|
19
19
|
|
|
20
20
|
export Carousel = component
|
|
21
|
-
@orientation :=
|
|
22
|
-
@loop := false
|
|
23
|
-
@autoplay := false
|
|
24
|
-
@interval := 4000
|
|
25
|
-
@label :=
|
|
21
|
+
@orientation:: "horizontal" | "vertical" := "horizontal"
|
|
22
|
+
@loop:: boolean := false
|
|
23
|
+
@autoplay:: boolean := false
|
|
24
|
+
@interval:: number := 4000
|
|
25
|
+
@label:: string := "Carousel"
|
|
26
26
|
|
|
27
27
|
activeIndex := 0
|
|
28
28
|
_ready := false
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
# div $value: "lettuce", "Lettuce"
|
|
12
12
|
|
|
13
13
|
export CheckboxGroup = component
|
|
14
|
-
@value := []
|
|
15
|
-
@disabled := false
|
|
16
|
-
@orientation :=
|
|
17
|
-
@label :=
|
|
14
|
+
@value:: any[] := []
|
|
15
|
+
@disabled:: boolean := false
|
|
16
|
+
@orientation:: "horizontal" | "vertical" := "vertical"
|
|
17
|
+
@label:: string := ""
|
|
18
18
|
|
|
19
19
|
_options ~=
|
|
20
20
|
return [] unless @_slot
|
|
@@ -30,18 +30,47 @@ export CheckboxGroup = component
|
|
|
30
30
|
@value = arr
|
|
31
31
|
@emit 'change', @value
|
|
32
32
|
|
|
33
|
+
_boxes: ->
|
|
34
|
+
Array.from(@_root?.querySelectorAll('[role="checkbox"]') or [])
|
|
35
|
+
|
|
36
|
+
_syncTabStops: (focusIdx = null) ->
|
|
37
|
+
boxes = @_boxes()
|
|
38
|
+
return unless boxes.length
|
|
39
|
+
idx = focusIdx
|
|
40
|
+
if idx is null
|
|
41
|
+
idx = boxes.indexOf(document.activeElement)
|
|
42
|
+
idx = 0 if idx < 0
|
|
43
|
+
boxes.forEach (box, i) -> box.tabIndex = if i is idx then 0 else -1
|
|
44
|
+
|
|
45
|
+
_focusIndex: (idx) ->
|
|
46
|
+
boxes = @_boxes()
|
|
47
|
+
return unless boxes.length
|
|
48
|
+
idx = Math.max(0, Math.min(idx, boxes.length - 1))
|
|
49
|
+
@_syncTabStops(idx)
|
|
50
|
+
boxes[idx]?.focus()
|
|
51
|
+
|
|
33
52
|
onKeydown: (e) ->
|
|
34
|
-
boxes = @
|
|
53
|
+
boxes = @_boxes()
|
|
35
54
|
return unless boxes?.length
|
|
36
55
|
focused = Array.from(boxes).indexOf(document.activeElement)
|
|
37
56
|
return if focused < 0
|
|
38
57
|
len = boxes.length
|
|
39
58
|
ARIA.rovingNav e, {
|
|
40
|
-
next: =>
|
|
41
|
-
prev: =>
|
|
42
|
-
first: =>
|
|
43
|
-
last: =>
|
|
44
|
-
},
|
|
59
|
+
next: => @_focusIndex((focused + 1) %% len)
|
|
60
|
+
prev: => @_focusIndex((focused - 1) %% len)
|
|
61
|
+
first: => @_focusIndex(0)
|
|
62
|
+
last: => @_focusIndex(len - 1)
|
|
63
|
+
}, @orientation
|
|
64
|
+
|
|
65
|
+
onFocusin: (e) ->
|
|
66
|
+
boxes = @_boxes()
|
|
67
|
+
idx = boxes.indexOf(e.target)
|
|
68
|
+
@_syncTabStops(idx) if idx >= 0
|
|
69
|
+
|
|
70
|
+
mounted: ->
|
|
71
|
+
requestAnimationFrame => @_syncTabStops()
|
|
72
|
+
|
|
73
|
+
~> @_syncTabStops()
|
|
45
74
|
|
|
46
75
|
render
|
|
47
76
|
div ref: "_root", role: "group", aria-label: @label or undefined, aria-orientation: @orientation
|
|
@@ -52,7 +81,7 @@ export CheckboxGroup = component
|
|
|
52
81
|
slot
|
|
53
82
|
|
|
54
83
|
for opt, idx in _options
|
|
55
|
-
button role: "checkbox", tabindex:
|
|
84
|
+
button role: "checkbox", tabindex: "-1"
|
|
56
85
|
aria-checked: opt.dataset.value in @value
|
|
57
86
|
$checked: (opt.dataset.value in @value)?!
|
|
58
87
|
$disabled: @disabled?!
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
# span "Dark mode"
|
|
13
13
|
|
|
14
14
|
export Checkbox = component
|
|
15
|
-
@checked := false
|
|
16
|
-
@disabled := false
|
|
17
|
-
@indeterminate := false
|
|
18
|
-
@switch := false
|
|
15
|
+
@checked:: boolean := false
|
|
16
|
+
@disabled:: boolean := false
|
|
17
|
+
@indeterminate:: boolean := false
|
|
18
|
+
@switch:: boolean := false
|
|
19
19
|
|
|
20
20
|
onClick: ->
|
|
21
21
|
return if @disabled
|
|
@@ -10,14 +10,16 @@
|
|
|
10
10
|
# Combobox query <=> searchText, items: fruits, @select: handleSelect, @filter: filterFn
|
|
11
11
|
|
|
12
12
|
export Combobox = component
|
|
13
|
-
@query :=
|
|
14
|
-
@items := []
|
|
15
|
-
@placeholder :=
|
|
16
|
-
@disabled := false
|
|
17
|
-
@autoHighlight := true
|
|
13
|
+
@query:: string := ""
|
|
14
|
+
@items:: any[] := []
|
|
15
|
+
@placeholder:: string := "Search..."
|
|
16
|
+
@disabled:: boolean := false
|
|
17
|
+
@autoHighlight:: boolean := true
|
|
18
18
|
|
|
19
19
|
open := false
|
|
20
20
|
highlightedIndex := -1
|
|
21
|
+
_ready := false
|
|
22
|
+
_popupGuard =! ARIA.popupGuard()
|
|
21
23
|
_listId =! "cb-#{Math.random().toString(36).slice(2, 8)}"
|
|
22
24
|
|
|
23
25
|
getItems: ->
|
|
@@ -38,31 +40,34 @@ export Combobox = component
|
|
|
38
40
|
highlightedIndex = if @autoHighlight and @items.length > 0 then 0 else -1
|
|
39
41
|
@emit 'filter', @query
|
|
40
42
|
|
|
41
|
-
onFocusin: ->
|
|
43
|
+
onFocusin: ->
|
|
44
|
+
return unless _popupGuard.canOpen()
|
|
45
|
+
@openMenu()
|
|
46
|
+
|
|
47
|
+
onInputClick: ->
|
|
48
|
+
return if @disabled or open
|
|
49
|
+
@openMenu(true)
|
|
42
50
|
|
|
43
51
|
onFocusout: ->
|
|
44
|
-
setTimeout => @close() unless @_content?.contains(document.activeElement), 0
|
|
52
|
+
setTimeout => @close(false, true) unless @_content?.contains(document.activeElement), 0
|
|
45
53
|
|
|
46
|
-
openMenu: ->
|
|
54
|
+
openMenu: (force = false) ->
|
|
55
|
+
return unless force or _popupGuard.canOpen()
|
|
47
56
|
open = true
|
|
48
57
|
highlightedIndex = -1
|
|
49
58
|
@_input?.focus()
|
|
50
59
|
|
|
51
|
-
close: ->
|
|
60
|
+
close: (restoreFocus = true, blockOpen = false) ->
|
|
52
61
|
open = false
|
|
53
62
|
highlightedIndex = -1
|
|
54
|
-
|
|
63
|
+
_popupGuard.block() if blockOpen
|
|
64
|
+
@_input?.focus() if restoreFocus
|
|
65
|
+
|
|
66
|
+
mounted: ->
|
|
67
|
+
_ready = true
|
|
55
68
|
|
|
56
69
|
_applyPlacement: ->
|
|
57
|
-
|
|
58
|
-
@_list.style.position = 'fixed'
|
|
59
|
-
@_list.style.inset = 'auto'
|
|
60
|
-
@_list.style.margin = '0'
|
|
61
|
-
@_list.style.positionArea = 'bottom start'
|
|
62
|
-
@_list.style.positionTry = 'flip-block, flip-inline, flip-block flip-inline'
|
|
63
|
-
@_list.style.positionVisibility = 'anchors-visible'
|
|
64
|
-
@_list.style.marginTop = '2px'
|
|
65
|
-
@_list.style.minWidth = 'anchor-size(width)'
|
|
70
|
+
ARIA.position @_content, @_list, placement: 'bottom start', offset: 2, matchWidth: true
|
|
66
71
|
|
|
67
72
|
isDisabled: (item) -> item?.hasAttribute?('data-disabled')
|
|
68
73
|
|
|
@@ -73,7 +78,7 @@ export Combobox = component
|
|
|
73
78
|
val = item.dataset.value ?? item.textContent.trim()
|
|
74
79
|
@query = val
|
|
75
80
|
@emit 'select', val
|
|
76
|
-
@close()
|
|
81
|
+
@close(true, true)
|
|
77
82
|
|
|
78
83
|
_nextEnabled: (from, dir) ->
|
|
79
84
|
opts = @getItems()
|
|
@@ -93,13 +98,14 @@ export Combobox = component
|
|
|
93
98
|
last: => highlightedIndex = len - 1; @_scrollToItem()
|
|
94
99
|
select: => if highlightedIndex >= 0 then @selectIndex(highlightedIndex) else if len is 1 then @selectIndex(0)
|
|
95
100
|
dismiss: => if open then @close() else @query = ''
|
|
96
|
-
tab: => @close()
|
|
101
|
+
tab: => @close(false, true)
|
|
97
102
|
|
|
98
103
|
~>
|
|
104
|
+
return unless _ready
|
|
99
105
|
if @_list
|
|
100
|
-
@_list.setAttribute 'popover', 'auto'
|
|
101
106
|
@_applyPlacement()
|
|
102
107
|
ARIA.bindPopover open, (=> @_list), ((isOpen) => open = isOpen), (=> @_input)
|
|
108
|
+
ARIA.popupDismiss open, (=> @_list), (=> @close(false, true)), [=> @_input, => @_content]
|
|
103
109
|
|
|
104
110
|
render
|
|
105
111
|
. ref: "_content", $open: open?!
|
|
@@ -118,13 +124,20 @@ export Combobox = component
|
|
|
118
124
|
disabled: @disabled
|
|
119
125
|
placeholder: @placeholder
|
|
120
126
|
value: @query
|
|
127
|
+
@click: @onInputClick
|
|
121
128
|
@keydown: @_onKeydown
|
|
122
129
|
if @query
|
|
123
130
|
button aria-label: "Clear", $clear: true, @click: @clear
|
|
124
131
|
"✕"
|
|
125
132
|
|
|
126
133
|
# Listbox
|
|
127
|
-
div ref: "_list"
|
|
134
|
+
div ref: "_list"
|
|
135
|
+
id: _listId
|
|
136
|
+
role: "listbox"
|
|
137
|
+
popover: "auto"
|
|
138
|
+
hidden: not open
|
|
139
|
+
aria-hidden: (open ? undefined : "true")
|
|
140
|
+
style: "position:fixed;margin:0;inset:auto"
|
|
128
141
|
$open: open?!
|
|
129
142
|
for item, idx in @items
|
|
130
143
|
div role: "option", tabindex: "-1", id: "#{_listId}-#{idx}"
|
|
@@ -37,11 +37,11 @@ dpInRange = (day, from, to) ->
|
|
|
37
37
|
t >= lo and t <= hi
|
|
38
38
|
|
|
39
39
|
export DatePicker = component
|
|
40
|
-
@value := null
|
|
41
|
-
@placeholder :=
|
|
42
|
-
@disabled := false
|
|
43
|
-
@range := false
|
|
44
|
-
@firstDayOfWeek := 0
|
|
40
|
+
@value:: any := null
|
|
41
|
+
@placeholder:: string := "mm/dd/yyyy"
|
|
42
|
+
@disabled:: boolean := false
|
|
43
|
+
@range:: boolean := false
|
|
44
|
+
@firstDayOfWeek:: number := 0
|
|
45
45
|
|
|
46
46
|
open := false
|
|
47
47
|
viewMonth := new Date()
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
# button @click: (=> showDialog = false), "Close"
|
|
14
14
|
|
|
15
15
|
export Dialog = component
|
|
16
|
-
@open := false
|
|
17
|
-
@dismissable := true
|
|
18
|
-
@initialFocus := null
|
|
16
|
+
@open:: boolean := false
|
|
17
|
+
@dismissable:: boolean := true
|
|
18
|
+
@initialFocus:: any := null
|
|
19
19
|
|
|
20
20
|
_id =! "dlg-#{Math.random().toString(36).slice(2, 8)}"
|
|
21
21
|
|
|
@@ -55,6 +55,10 @@ export Dialog = component
|
|
|
55
55
|
@_dialog?.close()
|
|
56
56
|
|
|
57
57
|
render
|
|
58
|
-
dialog ref: "_dialog"
|
|
58
|
+
dialog ref: "_dialog"
|
|
59
|
+
hidden: not @open
|
|
60
|
+
aria-hidden: (@open ? undefined : "true")
|
|
61
|
+
@click: @onBackdropClick
|
|
62
|
+
@keydown: @onKeydown
|
|
59
63
|
$open: @open?!
|
|
60
64
|
slot
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
# p "Panel content here"
|
|
11
11
|
|
|
12
12
|
export Drawer = component
|
|
13
|
-
@open := false
|
|
14
|
-
@side :=
|
|
15
|
-
@dismissable := true
|
|
13
|
+
@open:: boolean := false
|
|
14
|
+
@side:: "top" | "right" | "bottom" | "left" := "right"
|
|
15
|
+
@dismissable:: boolean := true
|
|
16
16
|
|
|
17
17
|
_prevFocus = null
|
|
18
18
|
_id =! "drw-#{Math.random().toString(36).slice(2, 8)}"
|
|
@@ -51,7 +51,11 @@ export Drawer = component
|
|
|
51
51
|
@_dialog?.close()
|
|
52
52
|
|
|
53
53
|
render
|
|
54
|
-
dialog ref: "_dialog"
|
|
54
|
+
dialog ref: "_dialog"
|
|
55
|
+
hidden: not @open
|
|
56
|
+
aria-hidden: (@open ? undefined : "true")
|
|
57
|
+
$open: @open?!
|
|
58
|
+
$side: @side
|
|
55
59
|
@click: @onBackdropClick
|
|
56
60
|
@keydown: @onKeydown
|
|
57
61
|
$side: @side
|
|
@@ -11,10 +11,11 @@
|
|
|
11
11
|
# input type: "text", value: name, @input: (e) => name = e.target.value
|
|
12
12
|
|
|
13
13
|
export EditableValue = component
|
|
14
|
-
@disabled := false
|
|
14
|
+
@disabled:: boolean := false
|
|
15
15
|
|
|
16
16
|
editing := false
|
|
17
17
|
saving := false
|
|
18
|
+
_ready := false
|
|
18
19
|
|
|
19
20
|
_onEdit: ->
|
|
20
21
|
return if @disabled
|
|
@@ -50,8 +51,13 @@ export EditableValue = component
|
|
|
50
51
|
editor.style.zIndex = '50'
|
|
51
52
|
editor.querySelector('input, textarea, select')?.focus()
|
|
52
53
|
|
|
54
|
+
mounted: ->
|
|
55
|
+
_ready = true
|
|
56
|
+
|
|
53
57
|
~>
|
|
58
|
+
_readyNow = _ready
|
|
54
59
|
_editing = editing # track before any early return
|
|
60
|
+
return unless _readyNow
|
|
55
61
|
display = @_root?.querySelector('[data-display]')
|
|
56
62
|
editor = @_root?.querySelector('[data-editor]')
|
|
57
63
|
return unless display and editor
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
# Input value <=> email, type: "email"
|
|
10
10
|
|
|
11
11
|
export Field = component
|
|
12
|
-
@label :=
|
|
13
|
-
@description :=
|
|
14
|
-
@error :=
|
|
15
|
-
@disabled := false
|
|
16
|
-
@required := false
|
|
12
|
+
@label:: string := ""
|
|
13
|
+
@description:: string := ""
|
|
14
|
+
@error:: string := ""
|
|
15
|
+
@disabled:: boolean := false
|
|
16
|
+
@required:: boolean := false
|
|
17
17
|
|
|
18
18
|
_id =! "fld-#{Math.random().toString(36).slice(2, 8)}"
|
|
19
19
|
|