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,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.
|
|
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('
|
|
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
|
|
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
|
|
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',
|
|
638
|
+
this.emit('IDENTIFIER', 'div#' + m[1]);
|
|
639
639
|
return m[0].length;
|
|
640
640
|
}
|
|
641
641
|
}
|