@rip-lang/ui 0.3.66 → 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.
Files changed (83) hide show
  1. package/AGENTS.md +93 -0
  2. package/README.md +22 -625
  3. package/browser/AGENTS.md +213 -0
  4. package/browser/CONTRIBUTING.md +375 -0
  5. package/browser/README.md +11 -0
  6. package/browser/TESTING.md +59 -0
  7. package/browser/browser.rip +56 -0
  8. package/{components → browser/components}/accordion.rip +1 -1
  9. package/{components → browser/components}/alert-dialog.rip +6 -3
  10. package/{components → browser/components}/autocomplete.rip +27 -21
  11. package/{components → browser/components}/avatar.rip +3 -3
  12. package/{components → browser/components}/badge.rip +1 -1
  13. package/{components → browser/components}/breadcrumb.rip +2 -2
  14. package/{components → browser/components}/button-group.rip +3 -3
  15. package/{components → browser/components}/button.rip +2 -2
  16. package/{components → browser/components}/card.rip +1 -1
  17. package/{components → browser/components}/carousel.rip +5 -5
  18. package/{components → browser/components}/checkbox-group.rip +40 -11
  19. package/{components → browser/components}/checkbox.rip +4 -4
  20. package/{components → browser/components}/collapsible.rip +2 -2
  21. package/{components → browser/components}/combobox.rip +36 -23
  22. package/{components → browser/components}/context-menu.rip +1 -1
  23. package/{components → browser/components}/date-picker.rip +5 -5
  24. package/{components → browser/components}/dialog.rip +8 -4
  25. package/{components → browser/components}/drawer.rip +8 -4
  26. package/{components → browser/components}/editable-value.rip +7 -1
  27. package/{components → browser/components}/field.rip +5 -5
  28. package/{components → browser/components}/fieldset.rip +2 -2
  29. package/{components → browser/components}/form.rip +1 -1
  30. package/{components → browser/components}/grid.rip +8 -8
  31. package/{components → browser/components}/input-group.rip +1 -1
  32. package/{components → browser/components}/input.rip +6 -6
  33. package/{components → browser/components}/label.rip +2 -2
  34. package/{components → browser/components}/menu.rip +17 -10
  35. package/{components → browser/components}/menubar.rip +1 -1
  36. package/{components → browser/components}/meter.rip +7 -7
  37. package/{components → browser/components}/multi-select.rip +76 -33
  38. package/{components → browser/components}/native-select.rip +3 -3
  39. package/{components → browser/components}/nav-menu.rip +3 -3
  40. package/{components → browser/components}/number-field.rip +11 -11
  41. package/{components → browser/components}/otp-field.rip +4 -4
  42. package/{components → browser/components}/pagination.rip +4 -4
  43. package/{components → browser/components}/popover.rip +11 -24
  44. package/{components → browser/components}/preview-card.rip +7 -11
  45. package/{components → browser/components}/progress.rip +3 -3
  46. package/{components → browser/components}/radio-group.rip +4 -4
  47. package/{components → browser/components}/resizable.rip +3 -3
  48. package/{components → browser/components}/scroll-area.rip +1 -1
  49. package/{components → browser/components}/select.rip +55 -27
  50. package/{components → browser/components}/separator.rip +2 -2
  51. package/{components → browser/components}/skeleton.rip +4 -4
  52. package/{components → browser/components}/slider.rip +15 -10
  53. package/{components → browser/components}/spinner.rip +2 -2
  54. package/{components → browser/components}/table.rip +2 -2
  55. package/{components → browser/components}/tabs.rip +12 -7
  56. package/{components → browser/components}/textarea.rip +8 -8
  57. package/{components → browser/components}/toast.rip +3 -3
  58. package/{components → browser/components}/toggle-group.rip +42 -11
  59. package/{components → browser/components}/toggle.rip +2 -2
  60. package/{components → browser/components}/toolbar.rip +2 -2
  61. package/{components → browser/components}/tooltip.rip +19 -23
  62. package/browser/hljs-rip.js +209 -0
  63. package/browser/playwright.config.mjs +31 -0
  64. package/browser/tests/overlays.js +349 -0
  65. package/email/AGENTS.md +16 -0
  66. package/email/README.md +55 -0
  67. package/email/benchmarks/benchmark.rip +94 -0
  68. package/email/benchmarks/samples.rip +104 -0
  69. package/email/compat.rip +129 -0
  70. package/email/components.rip +371 -0
  71. package/email/dom.rip +330 -0
  72. package/email/email.rip +10 -0
  73. package/email/render.rip +82 -0
  74. package/package.json +29 -39
  75. package/shared/README.md +3 -0
  76. package/shared/styles.rip +17 -0
  77. package/tailwind/AGENTS.md +3 -0
  78. package/tailwind/README.md +27 -0
  79. package/tailwind/engine.js +107 -0
  80. package/tailwind/inline.js +215 -0
  81. package/tailwind/serve.js +6 -0
  82. package/tailwind/tailwind.rip +13 -0
  83. 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'
@@ -16,7 +16,7 @@
16
16
  # p "Content B"
17
17
 
18
18
  export Accordion = component
19
- @multiple := false
19
+ @multiple:: boolean := false
20
20
 
21
21
  openItems := new Set()
22
22
  _ready := false
@@ -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", $open: @open?!
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 := 'Type to search...'
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?.focus()
56
+ @_input?.removeAttribute 'aria-activedescendant'
57
+ _popupGuard.block() if blockOpen
58
+ @_input?.focus() if restoreFocus
54
59
 
55
60
  _applyPlacement: ->
56
- return unless @_list
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
- div ref: "_list", id: _listId, role: "listbox", $open: open?!, style: "position:fixed;margin:0;inset:auto"
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))
@@ -9,9 +9,9 @@
9
9
  # Avatar
10
10
 
11
11
  export Avatar = component
12
- @src := ''
13
- @alt := ''
14
- @fallback := ''
12
+ @src:: string := ""
13
+ @alt:: string := ""
14
+ @fallback:: string := ""
15
15
 
16
16
  imgError := false
17
17
 
@@ -8,7 +8,7 @@
8
8
  # Badge variant: "outline", "Beta"
9
9
 
10
10
  export Badge = component
11
- @variant := 'solid'
11
+ @variant:: "solid" | "outline" | "subtle" := "solid"
12
12
 
13
13
  render
14
14
  span $variant: @variant
@@ -15,8 +15,8 @@
15
15
  # span $item: true, "Settings"
16
16
 
17
17
  export Breadcrumb = component
18
- @separator := '/'
19
- @label := 'Breadcrumb'
18
+ @separator:: string := "/"
19
+ @label:: string := "Breadcrumb"
20
20
 
21
21
  _ready := false
22
22
 
@@ -13,9 +13,9 @@
13
13
  # Toggle pressed <=> isItalic, "Italic"
14
14
 
15
15
  export ButtonGroup = component
16
- @orientation := 'horizontal'
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"
@@ -9,8 +9,8 @@
9
9
  # Button disabled: true
10
10
  # "Unavailable"
11
11
 
12
- export Button = component
13
- @disabled := false
12
+ export Button = component extends button
13
+ @disabled:: boolean := false
14
14
 
15
15
  onClick: ->
16
16
  return if @disabled
@@ -17,7 +17,7 @@
17
17
  # p "Clickable card"
18
18
 
19
19
  export Card = component
20
- @interactive := false
20
+ @interactive:: boolean := false
21
21
 
22
22
  render
23
23
  div tabindex: (if @interactive then "0" else undefined)
@@ -18,11 +18,11 @@
18
18
  # div $slide: true, "Slide B"
19
19
 
20
20
  export Carousel = component
21
- @orientation := 'horizontal'
22
- @loop := false
23
- @autoplay := false
24
- @interval := 4000
25
- @label := 'Carousel'
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 := 'vertical'
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 = @_root?.querySelectorAll('[role="checkbox"]')
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: => boxes[(focused + 1) %% len]?.focus()
41
- prev: => boxes[(focused - 1) %% len]?.focus()
42
- first: => boxes[0]?.focus()
43
- last: => boxes[len - 1]?.focus()
44
- }, 'both'
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: (if idx is 0 then "0" else "-1")
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
@@ -11,8 +11,8 @@
11
11
  # p "Hidden content here"
12
12
 
13
13
  export Collapsible = component
14
- @open := false
15
- @disabled := false
14
+ @open:: boolean := false
15
+ @disabled:: boolean := false
16
16
 
17
17
  _ready := false
18
18
 
@@ -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 := 'Search...'
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: -> @openMenu()
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
- @_input?.focus()
63
+ _popupGuard.block() if blockOpen
64
+ @_input?.focus() if restoreFocus
65
+
66
+ mounted: ->
67
+ _ready = true
55
68
 
56
69
  _applyPlacement: ->
57
- return unless @_list
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", id: _listId, role: "listbox", style: "position:fixed;margin:0;inset:auto"
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}"
@@ -12,7 +12,7 @@
12
12
  # div $item: "paste", "Paste"
13
13
 
14
14
  export ContextMenu = component
15
- @disabled := false
15
+ @disabled:: boolean := false
16
16
 
17
17
  open := false
18
18
  highlightedIndex := -1
@@ -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 := 'mm/dd/yyyy'
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", @click: @onBackdropClick, @keydown: @onKeydown
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 := 'right'
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", $open: @open?!, $side: @side
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
 
@@ -11,8 +11,8 @@
11
11
  # Input value <=> city
12
12
 
13
13
  export Fieldset = component
14
- @legend := ''
15
- @disabled := false
14
+ @legend:: string := ""
15
+ @disabled:: boolean := false
16
16
 
17
17
  render
18
18
  fieldset disabled: @disabled, $disabled: @disabled?!
@@ -12,7 +12,7 @@
12
12
  # "Submit"
13
13
 
14
14
  export Form = component
15
- @disabled := false
15
+ @disabled:: boolean := false
16
16
 
17
17
  submitting := false
18
18
  submitted := false