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.
Files changed (49) hide show
  1. package/README.md +1 -1
  2. package/docs/dist/rip.js +28 -8
  3. package/docs/dist/rip.min.js +16 -16
  4. package/docs/dist/rip.min.js.br +0 -0
  5. package/docs/index.html +4 -0
  6. package/docs/ui/accordion.rip +113 -0
  7. package/docs/ui/autocomplete.rip +141 -0
  8. package/docs/ui/avatar.rip +37 -0
  9. package/docs/ui/button.rip +23 -0
  10. package/docs/ui/checkbox-group.rip +65 -0
  11. package/docs/ui/checkbox.rip +33 -0
  12. package/docs/ui/combobox.rip +155 -0
  13. package/docs/ui/context-menu.rip +105 -0
  14. package/docs/ui/date-picker.rip +214 -0
  15. package/docs/ui/dialog.rip +107 -0
  16. package/docs/ui/drawer.rip +79 -0
  17. package/docs/ui/editable-value.rip +80 -0
  18. package/docs/ui/field.rip +53 -0
  19. package/docs/ui/fieldset.rip +22 -0
  20. package/docs/ui/form.rip +39 -0
  21. package/docs/ui/grid.rip +901 -0
  22. package/docs/ui/index.css +1379 -0
  23. package/docs/ui/index.html +2097 -0
  24. package/docs/ui/input.rip +36 -0
  25. package/docs/ui/menu.rip +162 -0
  26. package/docs/ui/menubar.rip +155 -0
  27. package/docs/ui/meter.rip +36 -0
  28. package/docs/ui/multi-select.rip +158 -0
  29. package/docs/ui/nav-menu.rip +129 -0
  30. package/docs/ui/number-field.rip +162 -0
  31. package/docs/ui/otp-field.rip +89 -0
  32. package/docs/ui/popover.rip +143 -0
  33. package/docs/ui/preview-card.rip +73 -0
  34. package/docs/ui/progress.rip +25 -0
  35. package/docs/ui/radio-group.rip +67 -0
  36. package/docs/ui/scroll-area.rip +145 -0
  37. package/docs/ui/select.rip +184 -0
  38. package/docs/ui/separator.rip +17 -0
  39. package/docs/ui/slider.rip +165 -0
  40. package/docs/ui/tabs.rip +124 -0
  41. package/docs/ui/toast.rip +87 -0
  42. package/docs/ui/toggle-group.rip +78 -0
  43. package/docs/ui/toggle.rip +24 -0
  44. package/docs/ui/toolbar.rip +46 -0
  45. package/docs/ui/tooltip.rip +115 -0
  46. package/package.json +2 -1
  47. package/src/app.rip +1 -1
  48. package/src/components.js +3 -4
  49. package/src/lexer.js +1 -1
@@ -0,0 +1,46 @@
1
+ # Toolbar — accessible headless toolbar
2
+ #
3
+ # Groups interactive controls with roving tabindex keyboard navigation.
4
+ # Arrow keys move focus between focusable children. Ships zero CSS.
5
+ #
6
+ # Usage:
7
+ # Toolbar
8
+ # Button @click: save, "Save"
9
+ # Button @click: undo, "Undo"
10
+ # Separator orientation: "vertical"
11
+ # Toggle pressed <=> isBold, "Bold"
12
+
13
+ export Toolbar = component
14
+ @orientation := 'horizontal'
15
+ @label := ''
16
+
17
+ _getFocusable: ->
18
+ return [] unless @_root
19
+ Array.from(@_root.querySelectorAll('button, [tabindex], input, select, textarea')).filter (el) ->
20
+ not el.disabled and el.offsetParent isnt null
21
+
22
+ onKeydown: (e) ->
23
+ els = @_getFocusable()
24
+ return unless els.length
25
+ focused = els.indexOf(document.activeElement)
26
+ return if focused < 0
27
+ len = els.length
28
+ horiz = @orientation is 'horizontal'
29
+ switch e.key
30
+ when (if horiz then 'ArrowRight' else 'ArrowDown')
31
+ e.preventDefault()
32
+ els[(focused + 1) %% len]?.focus()
33
+ when (if horiz then 'ArrowLeft' else 'ArrowUp')
34
+ e.preventDefault()
35
+ els[(focused - 1) %% len]?.focus()
36
+ when 'Home'
37
+ e.preventDefault()
38
+ els[0]?.focus()
39
+ when 'End'
40
+ e.preventDefault()
41
+ els[len - 1]?.focus()
42
+
43
+ render
44
+ div role: "toolbar", aria-label: @label or undefined, aria-orientation: @orientation
45
+ $orientation: @orientation
46
+ slot
@@ -0,0 +1,115 @@
1
+ # Tooltip — accessible headless tooltip with delay and positioning
2
+ #
3
+ # Shows on hover/focus with configurable delay. Uses aria-describedby.
4
+ # Exposes $open, $entering, $exiting. Ships zero CSS.
5
+ #
6
+ # Usage:
7
+ # Tooltip text: "Helpful info", placement: "top"
8
+ # button "Hover me"
9
+
10
+ lastCloseTime = 0
11
+ GROUP_TIMEOUT = 400
12
+
13
+ export Tooltip = component
14
+ @text := ''
15
+ @placement := 'top'
16
+ @delay := 300
17
+ @offset := 6
18
+ @hoverable := false
19
+
20
+ open := false
21
+ entering := false
22
+ exiting := false
23
+ _showTimer := null
24
+ _hideTimer := null
25
+ _id =! "tip-#{Math.random().toString(36).slice(2, 8)}"
26
+
27
+ show: ->
28
+ clearTimeout _hideTimer if _hideTimer
29
+ delay = if (Date.now() - lastCloseTime) < GROUP_TIMEOUT then 0 else @delay
30
+ _showTimer = setTimeout =>
31
+ open = true
32
+ entering = true
33
+ setTimeout =>
34
+ entering = false
35
+ @_position()
36
+ , 0
37
+ , delay
38
+
39
+ hide: ->
40
+ clearTimeout _showTimer if _showTimer
41
+ exiting = true
42
+ _hideTimer = setTimeout =>
43
+ open = false
44
+ exiting = false
45
+ lastCloseTime = Date.now()
46
+ , 150
47
+
48
+ _cancelHide: ->
49
+ clearTimeout _hideTimer if _hideTimer
50
+ exiting = false
51
+
52
+ _position: ->
53
+ return unless @_trigger and @_tip
54
+ tr = @_trigger.getBoundingClientRect()
55
+ fl = @_tip.getBoundingClientRect()
56
+ [side, align] = @placement.split('-')
57
+ align ?= 'center'
58
+ gap = @offset
59
+
60
+ x = switch side
61
+ when 'bottom', 'top'
62
+ switch align
63
+ when 'start' then tr.left
64
+ when 'end' then tr.right - fl.width
65
+ else tr.left + (tr.width - fl.width) / 2
66
+ when 'right' then tr.right + gap
67
+ when 'left' then tr.left - fl.width - gap
68
+
69
+ y = switch side
70
+ when 'bottom' then tr.bottom + gap
71
+ when 'top' then tr.top - fl.height - gap
72
+ when 'left', 'right'
73
+ switch align
74
+ when 'start' then tr.top
75
+ when 'end' then tr.bottom - fl.height
76
+ else tr.top + (tr.height - fl.height) / 2
77
+
78
+ if side is 'bottom' and y + fl.height > window.innerHeight
79
+ y = tr.top - fl.height - gap
80
+ if side is 'top' and y < 0
81
+ y = tr.bottom + gap
82
+ if side is 'right' and x + fl.width > window.innerWidth
83
+ x = tr.left - fl.width - gap
84
+ if side is 'left' and x < 0
85
+ x = tr.right + gap
86
+
87
+ x = Math.max(4, Math.min(x, window.innerWidth - fl.width - 4))
88
+
89
+ @_tip.style.position = 'fixed'
90
+ @_tip.style.left = "#{x}px"
91
+ @_tip.style.top = "#{y}px"
92
+
93
+ beforeUnmount: ->
94
+ clearTimeout _showTimer if _showTimer
95
+ clearTimeout _hideTimer if _hideTimer
96
+
97
+ render
98
+ .
99
+ div ref: "_trigger"
100
+ aria-describedby: open ? _id : undefined
101
+ @mouseenter: @show
102
+ @mouseleave: @hide
103
+ @focusin: @show
104
+ @focusout: @hide
105
+ slot
106
+
107
+ if open
108
+ div ref: "_tip", id: _id, role: "tooltip", style: "position:fixed"
109
+ $open: true
110
+ $entering: entering?!
111
+ $exiting: exiting?!
112
+ $placement: @placement
113
+ @mouseenter: (=> @_cancelHide() if @hoverable)
114
+ @mouseleave: (=> @hide() if @hoverable)
115
+ @text
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "3.13.65",
3
+ "version": "3.13.67",
4
4
  "description": "A modern language that compiles to JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
@@ -33,6 +33,7 @@
33
33
  "scripts": {
34
34
  "build": "bun scripts/build.js",
35
35
  "bump": "bun scripts/bump.js",
36
+ "gallery": "bun scripts/gallery.js",
36
37
  "parser": "bun src/grammar/solar.rip -o src/parser.js src/grammar/grammar.rip",
37
38
  "serve": "bun scripts/serve.js",
38
39
  "test": "bun test/runner.js",
package/src/app.rip CHANGED
@@ -757,7 +757,7 @@ export createRenderer = (opts = {}) ->
757
757
  inst.mount wrapper
758
758
  layoutInstances.push inst
759
759
 
760
- slot = wrapper.querySelector('slot') or wrapper
760
+ slot = wrapper.querySelector('#content') or wrapper
761
761
  mp = slot
762
762
 
763
763
  currentLayouts = [...layoutFiles]
package/src/components.js CHANGED
@@ -1361,7 +1361,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1361
1361
  }
1362
1362
 
1363
1363
  // Regular attribute
1364
- if (typeof key === 'string') {
1364
+ if (typeof key === 'string' || key instanceof String) {
1365
1365
  // Strip quotes from string keys (e.g., "data-slot" → data-slot)
1366
1366
  if (key.startsWith('"') && key.endsWith('"')) {
1367
1367
  key = key.slice(1, -1);
@@ -1455,7 +1455,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1455
1455
  }
1456
1456
  } else {
1457
1457
  if (Array.isArray(value) && value[0] === 'presence') {
1458
- this._createLines.push(`{ const __v = ${valueCode}; __v == null ? void 0 : ${elVar}.setAttribute('${key}', __v); }`);
1458
+ this._createLines.push(`{ const __v = ${valueCode}; if (__v != null) ${elVar}.setAttribute('${key}', __v); }`);
1459
1459
  } else {
1460
1460
  this._createLines.push(`${elVar}.setAttribute('${key}', ${valueCode});`);
1461
1461
  }
@@ -1909,8 +1909,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1909
1909
  // Methods may read reactive state internally — treat as reactive so the
1910
1910
  // call gets wrapped in __effect and re-runs when dependencies change.
1911
1911
  if (Array.isArray(sexpr[0]) && sexpr[0][0] === '.' && sexpr[0][1] === 'this') {
1912
- const m = sexpr[0][2];
1913
- const name = typeof m === 'string' ? m : m instanceof String ? m.valueOf() : null;
1912
+ const name = _str(sexpr[0][2]);
1914
1913
  if (name && this.componentMembers?.has(name)) return true;
1915
1914
  }
1916
1915
 
package/src/lexer.js CHANGED
@@ -635,7 +635,7 @@ export class Lexer {
635
635
  return 0; // after a tag (div#main) → let # become a token for rewriter
636
636
  let m = /^#([a-zA-Z_][\w-]*)/.exec(this.chunk);
637
637
  if (m) {
638
- this.emit('IDENTIFIER', m[1] === 'content' ? 'slot' : 'div#' + m[1]);
638
+ this.emit('IDENTIFIER', 'div#' + m[1]);
639
639
  return m[0].length;
640
640
  }
641
641
  }