rip-lang 3.13.71 → 3.13.73
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 +25 -2
- package/docs/dist/rip.min.js +2 -2
- package/docs/dist/rip.min.js.br +0 -0
- package/docs/ui/alert-dialog.rip +96 -0
- package/docs/ui/badge.rip +15 -0
- package/docs/ui/breadcrumb.rip +46 -0
- package/docs/ui/button-group.rip +26 -0
- package/docs/ui/card.rip +25 -0
- package/docs/ui/carousel.rip +110 -0
- package/docs/ui/collapsible.rip +50 -0
- package/docs/ui/hljs-rip.js +209 -0
- package/docs/ui/index.css +412 -19
- package/docs/ui/index.html +690 -354
- package/docs/ui/input-group.rip +28 -0
- package/docs/ui/label.rip +16 -0
- package/docs/ui/native-select.rip +32 -0
- package/docs/ui/pagination.rip +123 -0
- package/docs/ui/resizable.rip +123 -0
- package/docs/ui/skeleton.rip +22 -0
- package/docs/ui/spinner.rip +17 -0
- package/docs/ui/table.rip +27 -0
- package/docs/ui/textarea.rip +48 -0
- package/package.json +1 -1
- package/src/browser.js +23 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# InputGroup — accessible headless input with prefix/suffix
|
|
2
|
+
#
|
|
3
|
+
# Wraps a form control with optional prefix and suffix elements.
|
|
4
|
+
# Use $prefix and $suffix on children to mark addon positions.
|
|
5
|
+
# Tracks child input focus for styling. Ships zero CSS.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# InputGroup
|
|
9
|
+
# span $prefix: true, "$"
|
|
10
|
+
# Input value <=> amount, type: "number"
|
|
11
|
+
# InputGroup
|
|
12
|
+
# Input value <=> search, placeholder: "Search..."
|
|
13
|
+
# button $suffix: true, @click: doSearch, "Go"
|
|
14
|
+
|
|
15
|
+
export InputGroup = component
|
|
16
|
+
@disabled := false
|
|
17
|
+
|
|
18
|
+
focused := false
|
|
19
|
+
|
|
20
|
+
mounted: ->
|
|
21
|
+
ctrl = @_root?.querySelector('input, select, textarea')
|
|
22
|
+
return unless ctrl
|
|
23
|
+
ctrl.addEventListener 'focusin', => focused = true
|
|
24
|
+
ctrl.addEventListener 'focusout', => focused = false
|
|
25
|
+
|
|
26
|
+
render
|
|
27
|
+
div ref: "_root", $disabled: @disabled?!, $focused: focused?!
|
|
28
|
+
slot
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Label — accessible headless form label
|
|
2
|
+
#
|
|
3
|
+
# Standalone label element that associates with a form control via @for.
|
|
4
|
+
# Companions Field but works independently. Ships zero CSS.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# Label for: "email-input", "Email address"
|
|
8
|
+
# Label required: true, "Username"
|
|
9
|
+
|
|
10
|
+
export Label = component
|
|
11
|
+
@for := null
|
|
12
|
+
@required := false
|
|
13
|
+
|
|
14
|
+
render
|
|
15
|
+
label for: @for?!, $required: @required?!
|
|
16
|
+
slot
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# NativeSelect — accessible headless native select wrapper
|
|
2
|
+
#
|
|
3
|
+
# Wraps a native <select> element with state tracking via data attributes.
|
|
4
|
+
# Use when the browser's built-in dropdown is preferred. Ships zero CSS.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# NativeSelect value <=> role, @change: handleChange
|
|
8
|
+
# option value: "", "Choose a role..."
|
|
9
|
+
# option value: "admin", "Admin"
|
|
10
|
+
# option value: "user", "User"
|
|
11
|
+
|
|
12
|
+
export NativeSelect = component
|
|
13
|
+
@value := ''
|
|
14
|
+
@disabled := false
|
|
15
|
+
@required := false
|
|
16
|
+
|
|
17
|
+
focused := false
|
|
18
|
+
|
|
19
|
+
onChange: (e) ->
|
|
20
|
+
@value = e.target.value
|
|
21
|
+
@emit 'change', @value
|
|
22
|
+
|
|
23
|
+
render
|
|
24
|
+
select value: @value, disabled: @disabled, required: @required
|
|
25
|
+
aria-disabled: @disabled?!
|
|
26
|
+
aria-required: @required?!
|
|
27
|
+
$disabled: @disabled?!
|
|
28
|
+
$focused: focused?!
|
|
29
|
+
@change: @onChange
|
|
30
|
+
@focusin: (=> focused = true)
|
|
31
|
+
@focusout: (=> focused = false)
|
|
32
|
+
slot
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Pagination — accessible headless page navigation
|
|
2
|
+
#
|
|
3
|
+
# Renders page buttons with prev/next and ellipsis gaps.
|
|
4
|
+
# Ships zero CSS.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# Pagination page <=> currentPage, total: 100, perPage: 10
|
|
8
|
+
# Pagination page <=> currentPage, total: 500, perPage: 20, siblingCount: 2
|
|
9
|
+
|
|
10
|
+
export Pagination = component
|
|
11
|
+
@page := 1
|
|
12
|
+
@total := 0
|
|
13
|
+
@perPage := 10
|
|
14
|
+
@siblingCount := 1
|
|
15
|
+
|
|
16
|
+
totalPages ~= Math.max(1, Math.ceil(@total / @perPage))
|
|
17
|
+
_ready := false
|
|
18
|
+
|
|
19
|
+
_range: (start, fin) ->
|
|
20
|
+
len = fin - start + 1
|
|
21
|
+
Array.from {length: len}, (_, i) -> start + i
|
|
22
|
+
|
|
23
|
+
visiblePages ~=
|
|
24
|
+
tp = totalPages
|
|
25
|
+
sibs = @siblingCount
|
|
26
|
+
current = @page
|
|
27
|
+
|
|
28
|
+
totalNumbers = sibs * 2 + 5
|
|
29
|
+
return @_range(1, tp) if tp <= totalNumbers
|
|
30
|
+
|
|
31
|
+
leftSib = Math.max(current - sibs, 1)
|
|
32
|
+
rightSib = Math.min(current + sibs, tp)
|
|
33
|
+
|
|
34
|
+
showLeftDots = leftSib > 2
|
|
35
|
+
showRightDots = rightSib < tp - 1
|
|
36
|
+
|
|
37
|
+
if not showLeftDots and showRightDots
|
|
38
|
+
leftCount = 3 + 2 * sibs
|
|
39
|
+
leftRange = @_range(1, leftCount)
|
|
40
|
+
return [...leftRange, -1, tp]
|
|
41
|
+
|
|
42
|
+
if showLeftDots and not showRightDots
|
|
43
|
+
rightCount = 3 + 2 * sibs
|
|
44
|
+
rightRange = @_range(tp - rightCount + 1, tp)
|
|
45
|
+
return [1, -2, ...rightRange]
|
|
46
|
+
|
|
47
|
+
midRange = @_range(leftSib, rightSib)
|
|
48
|
+
[1, -2, ...midRange, -1, tp]
|
|
49
|
+
|
|
50
|
+
goto: (pg) ->
|
|
51
|
+
pg = Math.max(1, Math.min(pg, totalPages))
|
|
52
|
+
return if pg is @page
|
|
53
|
+
@page = pg
|
|
54
|
+
@emit 'change', @page
|
|
55
|
+
|
|
56
|
+
onKeydown: (e) ->
|
|
57
|
+
switch e.key
|
|
58
|
+
when 'ArrowLeft'
|
|
59
|
+
e.preventDefault()
|
|
60
|
+
@goto(@page - 1)
|
|
61
|
+
when 'ArrowRight'
|
|
62
|
+
e.preventDefault()
|
|
63
|
+
@goto(@page + 1)
|
|
64
|
+
when 'Home'
|
|
65
|
+
e.preventDefault()
|
|
66
|
+
@goto(1)
|
|
67
|
+
when 'End'
|
|
68
|
+
e.preventDefault()
|
|
69
|
+
@goto(totalPages)
|
|
70
|
+
|
|
71
|
+
mounted: ->
|
|
72
|
+
_ready = true
|
|
73
|
+
|
|
74
|
+
_prevPages = null
|
|
75
|
+
|
|
76
|
+
_rebuild: (inner) ->
|
|
77
|
+
frag = document.createDocumentFragment()
|
|
78
|
+
for pg in visiblePages
|
|
79
|
+
if pg < 0
|
|
80
|
+
el = document.createElement 'span'
|
|
81
|
+
el.setAttribute 'data-ellipsis', ''
|
|
82
|
+
el.textContent = '...'
|
|
83
|
+
else
|
|
84
|
+
el = document.createElement 'button'
|
|
85
|
+
el.setAttribute 'aria-label', "Page #{pg}"
|
|
86
|
+
el.setAttribute 'data-page', ''
|
|
87
|
+
el.textContent = "#{pg}"
|
|
88
|
+
el.addEventListener 'click', => @goto(pg)
|
|
89
|
+
frag.appendChild el
|
|
90
|
+
inner.replaceChildren frag
|
|
91
|
+
_prevPages = visiblePages.join ','
|
|
92
|
+
|
|
93
|
+
_syncActive: (inner) ->
|
|
94
|
+
for btn in inner.querySelectorAll('[data-page]')
|
|
95
|
+
pg = parseInt btn.textContent
|
|
96
|
+
if pg is @page
|
|
97
|
+
btn.setAttribute 'aria-current', 'page'
|
|
98
|
+
btn.setAttribute 'data-active', ''
|
|
99
|
+
else
|
|
100
|
+
btn.removeAttribute 'aria-current'
|
|
101
|
+
btn.removeAttribute 'data-active'
|
|
102
|
+
|
|
103
|
+
~>
|
|
104
|
+
return unless _ready
|
|
105
|
+
inner = @_nav?.querySelector('[data-pages]')
|
|
106
|
+
return unless inner
|
|
107
|
+
|
|
108
|
+
key = visiblePages.join ','
|
|
109
|
+
if key isnt _prevPages
|
|
110
|
+
@_rebuild inner
|
|
111
|
+
@_syncActive inner
|
|
112
|
+
|
|
113
|
+
render
|
|
114
|
+
nav ref: "_nav", aria-label: "Pagination", @keydown: @onKeydown
|
|
115
|
+
button $prev: true, aria-label: "Previous page"
|
|
116
|
+
disabled: @page <= 1
|
|
117
|
+
$disabled: (@page <= 1)?!
|
|
118
|
+
@click: (=> @goto(@page - 1))
|
|
119
|
+
. $pages: true
|
|
120
|
+
button $next: true, aria-label: "Next page"
|
|
121
|
+
disabled: @page >= totalPages
|
|
122
|
+
$disabled: (@page >= totalPages)?!
|
|
123
|
+
@click: (=> @goto(@page + 1))
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Resizable — accessible headless resizable panels
|
|
2
|
+
#
|
|
3
|
+
# Container with draggable handles between panels for resizing.
|
|
4
|
+
# Panel sizes are stored as percentages and exposed via CSS
|
|
5
|
+
# custom properties. Place [data-handle] elements between [data-panel]
|
|
6
|
+
# elements. Ships zero CSS.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# Resizable
|
|
10
|
+
# div $panel: true
|
|
11
|
+
# p "Left panel"
|
|
12
|
+
# div $handle: true
|
|
13
|
+
# div $panel: true
|
|
14
|
+
# p "Right panel"
|
|
15
|
+
#
|
|
16
|
+
# Resizable orientation: "vertical", minSize: 20
|
|
17
|
+
# div $panel: true, "Top"
|
|
18
|
+
# div $handle: true
|
|
19
|
+
# div $panel: true, "Bottom"
|
|
20
|
+
|
|
21
|
+
export Resizable = component
|
|
22
|
+
@orientation := 'horizontal'
|
|
23
|
+
@minSize := 10
|
|
24
|
+
@maxSize := 90
|
|
25
|
+
|
|
26
|
+
_ready := false
|
|
27
|
+
_dragging = null
|
|
28
|
+
_startPos = 0
|
|
29
|
+
_startSizes = []
|
|
30
|
+
sizes := []
|
|
31
|
+
|
|
32
|
+
_panels: ->
|
|
33
|
+
return [] unless @_root
|
|
34
|
+
Array.from(@_root.querySelectorAll(':scope > [data-panel]') or [])
|
|
35
|
+
|
|
36
|
+
_handles: ->
|
|
37
|
+
return [] unless @_root
|
|
38
|
+
Array.from(@_root.querySelectorAll(':scope > [data-handle]') or [])
|
|
39
|
+
|
|
40
|
+
mounted: ->
|
|
41
|
+
_ready = true
|
|
42
|
+
panels = @_panels()
|
|
43
|
+
count = panels.length
|
|
44
|
+
if count and not sizes.length
|
|
45
|
+
even = 100 / count
|
|
46
|
+
sizes = Array.from {length: count}, -> even
|
|
47
|
+
|
|
48
|
+
@_handles().forEach (handle, idx) =>
|
|
49
|
+
handle.setAttribute 'role', 'separator'
|
|
50
|
+
handle.setAttribute 'tabindex', '0'
|
|
51
|
+
handle.addEventListener 'pointerdown', (e) => @_onPointerDown(idx, e)
|
|
52
|
+
handle.addEventListener 'keydown', (e) => @_onKeydown(idx, e)
|
|
53
|
+
|
|
54
|
+
_getPos: (e) ->
|
|
55
|
+
if @orientation is 'horizontal' then e.clientX else e.clientY
|
|
56
|
+
|
|
57
|
+
_getContainerSize: ->
|
|
58
|
+
rect = @_root?.getBoundingClientRect()
|
|
59
|
+
return 0 unless rect
|
|
60
|
+
if @orientation is 'horizontal' then rect.width else rect.height
|
|
61
|
+
|
|
62
|
+
_onPointerDown: (handleIdx, e) ->
|
|
63
|
+
e.preventDefault()
|
|
64
|
+
_dragging = handleIdx
|
|
65
|
+
_startPos = @_getPos(e)
|
|
66
|
+
_startSizes = [...sizes]
|
|
67
|
+
e.target.setPointerCapture(e.pointerId)
|
|
68
|
+
e.target.toggleAttribute 'data-dragging', true
|
|
69
|
+
|
|
70
|
+
_onPointerMove: (e) ->
|
|
71
|
+
return unless _dragging?
|
|
72
|
+
total = @_getContainerSize()
|
|
73
|
+
return unless total
|
|
74
|
+
delta = @_getPos(e) - _startPos
|
|
75
|
+
pctDelta = (delta / total) * 100
|
|
76
|
+
@_applyResize(_dragging, _startSizes[_dragging] + pctDelta, _startSizes[_dragging + 1] - pctDelta)
|
|
77
|
+
|
|
78
|
+
_onPointerUp: (e) ->
|
|
79
|
+
return unless _dragging?
|
|
80
|
+
handle = @_handles()[_dragging]
|
|
81
|
+
handle?.removeAttribute 'data-dragging'
|
|
82
|
+
_dragging = null
|
|
83
|
+
@emit 'resize', sizes
|
|
84
|
+
|
|
85
|
+
_applyResize: (idx, newLeft, newRight) ->
|
|
86
|
+
newLeft = Math.max(@minSize, Math.min(@maxSize, newLeft))
|
|
87
|
+
newRight = Math.max(@minSize, Math.min(@maxSize, newRight))
|
|
88
|
+
combined = sizes[idx] + sizes[idx + 1]
|
|
89
|
+
newRight = combined - newLeft
|
|
90
|
+
return if newRight < @minSize or newRight > @maxSize
|
|
91
|
+
updated = [...sizes]
|
|
92
|
+
updated[idx] = newLeft
|
|
93
|
+
updated[idx + 1] = newRight
|
|
94
|
+
sizes = updated
|
|
95
|
+
|
|
96
|
+
_onKeydown: (handleIdx, e) ->
|
|
97
|
+
step = 10
|
|
98
|
+
horiz = @orientation is 'horizontal'
|
|
99
|
+
delta = switch e.key
|
|
100
|
+
when (if horiz then 'ArrowRight' else 'ArrowDown') then step
|
|
101
|
+
when (if horiz then 'ArrowLeft' else 'ArrowUp') then -step
|
|
102
|
+
else null
|
|
103
|
+
return unless delta?
|
|
104
|
+
e.preventDefault()
|
|
105
|
+
@_applyResize(handleIdx, sizes[handleIdx] + delta, sizes[handleIdx + 1] - delta)
|
|
106
|
+
@emit 'resize', sizes
|
|
107
|
+
|
|
108
|
+
~>
|
|
109
|
+
return unless _ready
|
|
110
|
+
@_panels().forEach (el, idx) =>
|
|
111
|
+
pct = sizes[idx] or 0
|
|
112
|
+
el.style.setProperty '--panel-size', "#{pct}%"
|
|
113
|
+
el.style.flexBasis = "#{pct}%"
|
|
114
|
+
@_handles().forEach (handle, idx) =>
|
|
115
|
+
handle.setAttribute 'aria-valuenow', Math.round(sizes[idx] or 0)
|
|
116
|
+
handle.setAttribute 'aria-orientation', @orientation
|
|
117
|
+
|
|
118
|
+
render
|
|
119
|
+
div ref: "_root", $orientation: @orientation
|
|
120
|
+
@pointermove: @_onPointerMove
|
|
121
|
+
@pointerup: @_onPointerUp
|
|
122
|
+
style: "display:flex; flex-direction:#{if @orientation is 'horizontal' then 'row' else 'column'}"
|
|
123
|
+
slot
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Skeleton — accessible headless loading placeholder
|
|
2
|
+
#
|
|
3
|
+
# Placeholder element shown while content is loading.
|
|
4
|
+
# Exposes dimensions as CSS custom properties for styling.
|
|
5
|
+
# Ships zero CSS.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# Skeleton
|
|
9
|
+
# Skeleton width: "200px", height: "1em"
|
|
10
|
+
# Skeleton circle: true, width: "48px"
|
|
11
|
+
|
|
12
|
+
export Skeleton = component
|
|
13
|
+
@width := null
|
|
14
|
+
@height := null
|
|
15
|
+
@circle := false
|
|
16
|
+
@label := 'Loading'
|
|
17
|
+
|
|
18
|
+
render
|
|
19
|
+
div role: "status", aria-busy: "true", aria-label: @label
|
|
20
|
+
style: "--skeleton-width: #{@width or 'auto'}; --skeleton-height: #{@height or 'auto'}"
|
|
21
|
+
$circle: @circle?!
|
|
22
|
+
slot
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Spinner — accessible headless loading indicator
|
|
2
|
+
#
|
|
3
|
+
# Announces loading state to screen readers via role="status".
|
|
4
|
+
# Exposes size as a CSS custom property for styling.
|
|
5
|
+
# Ships zero CSS.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# Spinner
|
|
9
|
+
# Spinner label: "Saving...", size: "24px"
|
|
10
|
+
|
|
11
|
+
export Spinner = component
|
|
12
|
+
@label := 'Loading'
|
|
13
|
+
@size := null
|
|
14
|
+
|
|
15
|
+
render
|
|
16
|
+
div role: "status", aria-label: @label
|
|
17
|
+
style: if @size then "--spinner-size: #{@size}" else undefined
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Table — accessible headless semantic table wrapper
|
|
2
|
+
#
|
|
3
|
+
# Lightweight wrapper for HTML tables with optional caption and
|
|
4
|
+
# striped rows. For data-heavy tables with virtual scrolling, use Grid.
|
|
5
|
+
# Ships zero CSS.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# Table caption: "Team members", striped: true
|
|
9
|
+
# thead
|
|
10
|
+
# tr
|
|
11
|
+
# th "Name"
|
|
12
|
+
# th "Role"
|
|
13
|
+
# tbody
|
|
14
|
+
# tr
|
|
15
|
+
# td "Alice"
|
|
16
|
+
# td "Engineer"
|
|
17
|
+
|
|
18
|
+
export Table = component
|
|
19
|
+
@caption := ''
|
|
20
|
+
@striped := false
|
|
21
|
+
|
|
22
|
+
render
|
|
23
|
+
div $striped: @striped?!
|
|
24
|
+
table
|
|
25
|
+
if @caption
|
|
26
|
+
caption @caption
|
|
27
|
+
slot
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Textarea — accessible headless auto-resizing text area
|
|
2
|
+
#
|
|
3
|
+
# Tracks focus, validation, and disabled state via data attributes.
|
|
4
|
+
# Optional auto-resize adjusts height to fit content. Ships zero CSS.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# Textarea value <=> bio, placeholder: "Tell us about yourself"
|
|
8
|
+
# Textarea value <=> notes, autoResize: true, rows: 3
|
|
9
|
+
|
|
10
|
+
export Textarea = component
|
|
11
|
+
@value := ''
|
|
12
|
+
@placeholder := ''
|
|
13
|
+
@disabled := false
|
|
14
|
+
@required := false
|
|
15
|
+
@rows := 3
|
|
16
|
+
@autoResize := false
|
|
17
|
+
|
|
18
|
+
focused := false
|
|
19
|
+
touched := false
|
|
20
|
+
|
|
21
|
+
onInput: (e) ->
|
|
22
|
+
@value = e.target.value
|
|
23
|
+
@_resize(e.target) if @autoResize
|
|
24
|
+
|
|
25
|
+
onFocus: -> focused = true
|
|
26
|
+
onBlur: ->
|
|
27
|
+
focused = false
|
|
28
|
+
touched = true
|
|
29
|
+
|
|
30
|
+
_resize: (el) ->
|
|
31
|
+
el.style.height = 'auto'
|
|
32
|
+
el.style.height = "#{el.scrollHeight}px"
|
|
33
|
+
|
|
34
|
+
mounted: ->
|
|
35
|
+
@_resize(@_root) if @autoResize and @value
|
|
36
|
+
|
|
37
|
+
render
|
|
38
|
+
textarea ref: "_root", value: @value, placeholder: @placeholder, rows: @rows
|
|
39
|
+
disabled: @disabled
|
|
40
|
+
required: @required
|
|
41
|
+
aria-disabled: @disabled?!
|
|
42
|
+
aria-required: @required?!
|
|
43
|
+
$disabled: @disabled?!
|
|
44
|
+
$focused: focused?!
|
|
45
|
+
$touched: touched?!
|
|
46
|
+
@input: @onInput
|
|
47
|
+
@focusin: @onFocus
|
|
48
|
+
@focusout: @onBlur
|
package/package.json
CHANGED
package/src/browser.js
CHANGED
|
@@ -119,6 +119,29 @@ async function processRipScripts() {
|
|
|
119
119
|
await ui.launch('', opts);
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
+
|
|
123
|
+
// Step 6: data-reload enables SSE hot-reload from dev server
|
|
124
|
+
if (runtimeTag?.hasAttribute('data-reload')) {
|
|
125
|
+
let ready = false;
|
|
126
|
+
const es = new EventSource('/watch');
|
|
127
|
+
es.addEventListener('connected', () => {
|
|
128
|
+
if (ready) location.reload();
|
|
129
|
+
ready = true;
|
|
130
|
+
});
|
|
131
|
+
es.addEventListener('reload', (e) => {
|
|
132
|
+
if (e.data === 'styles') {
|
|
133
|
+
const t = Date.now();
|
|
134
|
+
document.querySelectorAll('link[rel="stylesheet"]').forEach(l => {
|
|
135
|
+
if (new URL(l.href).origin !== location.origin) return;
|
|
136
|
+
const url = new URL(l.href);
|
|
137
|
+
url.searchParams.set('_r', t);
|
|
138
|
+
l.href = url.toString();
|
|
139
|
+
});
|
|
140
|
+
} else {
|
|
141
|
+
location.reload();
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
122
145
|
}
|
|
123
146
|
|
|
124
147
|
export { processRipScripts };
|