rip-lang 3.10.7 → 3.10.9
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-ui.min.js +126 -126
- package/docs/dist/rip-ui.min.js.br +0 -0
- package/docs/dist/rip.browser.min.js +127 -127
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/results/index.html +57 -51
- package/package.json +1 -1
- package/src/browser.js +17 -2
- package/src/components.js +36 -7
- package/src/grammar/grammar.rip +9 -9
- package/src/grammar/lunar.rip +2 -2
- package/src/lexer.js +1 -1
- package/src/parser.js +6 -8
- package/src/types.js +2 -2
|
Binary file
|
package/docs/results/index.html
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
8
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
9
|
<link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,500;0,700;1,400&family=Nothing+You+Could+Do&display=swap" rel="stylesheet">
|
|
10
|
-
<script type="module" src="../dist/rip-ui.min.js"></script>
|
|
10
|
+
<script type="module" src="../dist/rip-ui.min.js" data-hash="true"></script>
|
|
11
11
|
<style>
|
|
12
12
|
/* ==========================================================================
|
|
13
13
|
Lab Results — Styles
|
|
@@ -434,6 +434,7 @@ body {
|
|
|
434
434
|
color: rgba(255,255,255,.85);
|
|
435
435
|
margin-top: 20px;
|
|
436
436
|
line-height: 1.5;
|
|
437
|
+
white-space: pre-line;
|
|
437
438
|
}
|
|
438
439
|
|
|
439
440
|
.detail__content {
|
|
@@ -500,6 +501,18 @@ body {
|
|
|
500
501
|
color: rgba(51,51,51,.5);
|
|
501
502
|
}
|
|
502
503
|
|
|
504
|
+
.detail__maintenance-icon + div {
|
|
505
|
+
max-width: 80%;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.detail__footer-image {
|
|
509
|
+
flex: 1;
|
|
510
|
+
min-height: 280px;
|
|
511
|
+
background-size: cover;
|
|
512
|
+
background-repeat: no-repeat;
|
|
513
|
+
background-position: center;
|
|
514
|
+
}
|
|
515
|
+
|
|
503
516
|
.gauge {
|
|
504
517
|
position: relative;
|
|
505
518
|
display: inline-block;
|
|
@@ -604,6 +617,8 @@ export Home = component
|
|
|
604
617
|
cholesterolHdlRatio: 4.3
|
|
605
618
|
glucose: 75
|
|
606
619
|
a1c: 5.2
|
|
620
|
+
waistCircumference: 36
|
|
621
|
+
bloodPressure: '118/76'
|
|
607
622
|
]
|
|
608
623
|
|
|
609
624
|
ranges :=
|
|
@@ -706,7 +721,9 @@ export Home = component
|
|
|
706
721
|
drawerOpen = false
|
|
707
722
|
mq.addEventListener 'change', handler
|
|
708
723
|
|
|
709
|
-
~>
|
|
724
|
+
~>
|
|
725
|
+
@setupResize()
|
|
726
|
+
window.app = { history, ranges, firstName, lastName, age, gender }
|
|
710
727
|
|
|
711
728
|
render
|
|
712
729
|
.app
|
|
@@ -908,28 +925,18 @@ export Summary = component
|
|
|
908
925
|
s = []
|
|
909
926
|
s.push { label: 'Heart Health', keys: ['triglycerides', 'hdlCholesterol', 'totalCholesterol', 'cholesterolHdlRatio'] }
|
|
910
927
|
s.push { label: 'Pancreas Health', keys: ['glucose', 'a1c'] }
|
|
911
|
-
|
|
912
|
-
s.push { label: 'Physical Measurements', keys: ['waistCircumference', 'bloodPressure'] }
|
|
928
|
+
s.push { label: 'Physical Measurements', keys: ['waistCircumference', 'bloodPressure'] } if history?[0]?.waistCircumference
|
|
913
929
|
s
|
|
914
930
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
result.push
|
|
925
|
-
section: section.label
|
|
926
|
-
key: k
|
|
927
|
-
name: ref.name
|
|
928
|
-
desc: ref.desc
|
|
929
|
-
value: String(val)
|
|
930
|
-
rowClass: if status is 'acceptable' then 'row-acceptable' else 'row-unacceptable'
|
|
931
|
-
icon: if status is 'acceptable' then checkIcon else warnIcon
|
|
932
|
-
result
|
|
931
|
+
rowClass: (k) ->
|
|
932
|
+
val = history[0]?[k]
|
|
933
|
+
return '' unless val?
|
|
934
|
+
if acceptable(k, val) is 'acceptable' then 'row-acceptable' else 'row-unacceptable'
|
|
935
|
+
|
|
936
|
+
rowIcon: (k) ->
|
|
937
|
+
val = history[0]?[k]
|
|
938
|
+
return '' unless val?
|
|
939
|
+
if acceptable(k, val) is 'acceptable' then checkIcon else warnIcon
|
|
933
940
|
|
|
934
941
|
render
|
|
935
942
|
.('brochure__page page-summary')
|
|
@@ -949,22 +956,27 @@ export Summary = component
|
|
|
949
956
|
|
|
950
957
|
.summary__copy "This report serves as an easy reference to review all of your testing results, including data from previous years. We encourage you to use this information in conjunction with an exam by your doctor, not as a replacement for one. We hope this summary will be a good starting point for conversations with your doctor about improving your overall health."
|
|
951
958
|
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
+
for section, i in sections
|
|
960
|
+
table.summary-table
|
|
961
|
+
unless i
|
|
962
|
+
thead
|
|
963
|
+
tr
|
|
964
|
+
th
|
|
965
|
+
th historyDate
|
|
966
|
+
tbody
|
|
959
967
|
tr
|
|
960
|
-
|
|
968
|
+
td section.label
|
|
961
969
|
td
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
.
|
|
967
|
-
|
|
970
|
+
for k in section.keys
|
|
971
|
+
tr
|
|
972
|
+
class: @rowClass(k)
|
|
973
|
+
td
|
|
974
|
+
.status-icon
|
|
975
|
+
innerHTML: @rowIcon(k)
|
|
976
|
+
div
|
|
977
|
+
.name ranges[k].name
|
|
978
|
+
.desc ranges[k].desc
|
|
979
|
+
td.value history[0]?[k]
|
|
968
980
|
</script>
|
|
969
981
|
|
|
970
982
|
<script type="text/rip" data-name="detail-page">
|
|
@@ -978,14 +990,14 @@ export DetailPage = component
|
|
|
978
990
|
|
|
979
991
|
shouldShow ~=
|
|
980
992
|
return true unless topic.conditional
|
|
981
|
-
history
|
|
993
|
+
history?[0]?[topic.conditional]?
|
|
982
994
|
|
|
983
995
|
tipIcons :=
|
|
984
996
|
utensils: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 2v7c0 1.1.9 2 2 2h4c1.1 0 2-.9 2-2V2"/><line x1="7" y1="2" x2="7" y2="22"/><path d="M21 15V2a5 5 0 00-5 5v6h4"/><line x1="21" y1="12" x2="21" y2="22"/></svg>'
|
|
985
|
-
drop:
|
|
986
|
-
walk:
|
|
987
|
-
doctor:
|
|
988
|
-
smile:
|
|
997
|
+
drop: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M12 2.69l5.66 5.66a8 8 0 11-11.31 0z"/></svg>'
|
|
998
|
+
walk: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="13" cy="4" r="2"/><path d="M7 21l3-4V11l-2-2-3 3"/><path d="M16 21l-2-6-3-1"/></svg>'
|
|
999
|
+
doctor: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M12 11V3M4.93 7.5l7.07 3.5 7.07-3.5"/><circle cx="12" cy="18" r="3"/><line x1="12" y1="15" x2="12" y2="11"/></svg>'
|
|
1000
|
+
smile: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="10"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/><line x1="9" y1="9" x2="9.01" y2="9"/><line x1="15" y1="9" x2="15.01" y2="9"/></svg>'
|
|
989
1001
|
|
|
990
1002
|
checkIcon := '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>'
|
|
991
1003
|
warnIcon := '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg>'
|
|
@@ -1008,9 +1020,6 @@ export DetailPage = component
|
|
|
1008
1020
|
.detail__header-title topic.title
|
|
1009
1021
|
.detail__header-copy topic.copy
|
|
1010
1022
|
|
|
1011
|
-
if topic.isImagePage
|
|
1012
|
-
.detail__header style: "background-image: url(#{topic.image}); background-size: cover"
|
|
1013
|
-
|
|
1014
1023
|
.detail__content
|
|
1015
1024
|
for bio in topic.biomarkers
|
|
1016
1025
|
.detail__item
|
|
@@ -1025,6 +1034,7 @@ export DetailPage = component
|
|
|
1025
1034
|
.desc ranges[bio.key].desc
|
|
1026
1035
|
|
|
1027
1036
|
.detail__item-info
|
|
1037
|
+
p ranges[bio.key].info[@statusOf(bio.key)]
|
|
1028
1038
|
p innerHTML: bio.text
|
|
1029
1039
|
if bio.extra
|
|
1030
1040
|
p bio.extra
|
|
@@ -1036,6 +1046,9 @@ export DetailPage = component
|
|
|
1036
1046
|
.detail__maintenance-icon
|
|
1037
1047
|
innerHTML: (tipIcons[tip.icon] or '')
|
|
1038
1048
|
div tip.text
|
|
1049
|
+
|
|
1050
|
+
if topic.isImagePage
|
|
1051
|
+
.detail__footer-image style: "background-image: url(#{topic.image})"
|
|
1039
1052
|
</script>
|
|
1040
1053
|
|
|
1041
1054
|
<script type="text/rip" data-name="gauge">
|
|
@@ -1106,12 +1119,5 @@ export Gauge = component
|
|
|
1106
1119
|
.gauge__units units
|
|
1107
1120
|
</script>
|
|
1108
1121
|
|
|
1109
|
-
<!-- ===== Boot ===== -->
|
|
1110
|
-
|
|
1111
|
-
<script type="text/rip">
|
|
1112
|
-
{ launch } = importRip! '/rip/ui.rip'
|
|
1113
|
-
launch hash: true
|
|
1114
|
-
</script>
|
|
1115
|
-
|
|
1116
1122
|
</body>
|
|
1117
1123
|
</html>
|
package/package.json
CHANGED
package/src/browser.js
CHANGED
|
@@ -136,12 +136,27 @@ if (typeof globalThis !== 'undefined') {
|
|
|
136
136
|
globalThis.__ripExports = { compile, compileToJS, formatSExpr, VERSION, BUILD_DATE, getReactiveRuntime, getComponentRuntime };
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
// Auto-
|
|
139
|
+
// Auto-launch: if rip-ui is bundled and the page has component scripts or a data-url,
|
|
140
|
+
// call launch() automatically with config from the script tag's data attributes.
|
|
141
|
+
async function autoLaunch() {
|
|
142
|
+
if (globalThis.__ripLaunched) return;
|
|
143
|
+
const ui = importRip.modules?.['ui.rip'];
|
|
144
|
+
if (!ui?.launch) return;
|
|
145
|
+
const cfg = document.querySelector('script[data-hash], script[data-url]');
|
|
146
|
+
const url = cfg?.getAttribute('data-url') || '';
|
|
147
|
+
const hasComponents = document.querySelectorAll('script[type="text/rip"][data-name]').length > 0;
|
|
148
|
+
if (!hasComponents && !url) return;
|
|
149
|
+
const opts = {};
|
|
150
|
+
if (cfg?.hasAttribute('data-hash')) opts.hash = cfg.getAttribute('data-hash') !== 'false';
|
|
151
|
+
await ui.launch(url, opts);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Auto-process <script type="text/rip"> blocks, then auto-launch if applicable.
|
|
140
155
|
// Deferred via queueMicrotask so bundled entry code (e.g. rip-ui.min.js registering
|
|
141
156
|
// importRip.modules) runs before script processing begins.
|
|
142
157
|
if (typeof document !== 'undefined') {
|
|
143
158
|
globalThis.__ripScriptsReady = new Promise(resolve => {
|
|
144
|
-
const run = () => processRipScripts().then(resolve);
|
|
159
|
+
const run = () => processRipScripts().then(autoLaunch).then(resolve);
|
|
145
160
|
if (document.readyState === 'loading') {
|
|
146
161
|
document.addEventListener('DOMContentLoaded', () => queueMicrotask(run));
|
|
147
162
|
} else {
|
package/src/components.js
CHANGED
|
@@ -492,7 +492,8 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
492
492
|
}
|
|
493
493
|
current = current[1];
|
|
494
494
|
}
|
|
495
|
-
let raw = typeof current === 'string' ? current : (current instanceof String ? current.valueOf() :
|
|
495
|
+
let raw = typeof current === 'string' ? current : (current instanceof String ? current.valueOf() : null);
|
|
496
|
+
if (raw === null) return { tag: null, classes, id: undefined };
|
|
496
497
|
// Split tag#id — e.g. "div#content" → tag: "div", id: "content"
|
|
497
498
|
let [tag, id] = raw.split('#');
|
|
498
499
|
if (!tag) tag = 'div'; // bare #id → div
|
|
@@ -1029,17 +1030,39 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1029
1030
|
if (id) {
|
|
1030
1031
|
this._createLines.push(`${elVar}.id = '${id}';`);
|
|
1031
1032
|
}
|
|
1033
|
+
|
|
1034
|
+
// Defer class emission when selector classes exist so class: attributes merge
|
|
1035
|
+
const prevClassArgs = this._pendingClassArgs;
|
|
1036
|
+
const prevClassEl = this._pendingClassEl;
|
|
1032
1037
|
if (classes.length > 0) {
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
} else {
|
|
1036
|
-
this._createLines.push(`${elVar}.className = '${classes.join(' ')}';`);
|
|
1037
|
-
}
|
|
1038
|
+
this._pendingClassArgs = [`'${classes.join(' ')}'`];
|
|
1039
|
+
this._pendingClassEl = elVar;
|
|
1038
1040
|
}
|
|
1039
1041
|
|
|
1040
1042
|
if (tag === 'svg') this._svgDepth = (this._svgDepth || 0) + 1;
|
|
1041
1043
|
this.appendChildren(elVar, args);
|
|
1042
1044
|
if (tag === 'svg') this._svgDepth--;
|
|
1045
|
+
|
|
1046
|
+
// Emit final class: if only selector classes (no dynamic additions), set statically
|
|
1047
|
+
if (classes.length > 0) {
|
|
1048
|
+
if (this._pendingClassArgs.length === 1) {
|
|
1049
|
+
if (isSvg) {
|
|
1050
|
+
this._createLines.push(`${elVar}.setAttribute('class', '${classes.join(' ')}');`);
|
|
1051
|
+
} else {
|
|
1052
|
+
this._createLines.push(`${elVar}.className = '${classes.join(' ')}';`);
|
|
1053
|
+
}
|
|
1054
|
+
} else {
|
|
1055
|
+
const combined = this._pendingClassArgs.join(', ');
|
|
1056
|
+
if (isSvg) {
|
|
1057
|
+
this._setupLines.push(`__effect(() => { ${elVar}.setAttribute('class', __clsx(${combined})); });`);
|
|
1058
|
+
} else {
|
|
1059
|
+
this._setupLines.push(`__effect(() => { ${elVar}.className = __clsx(${combined}); });`);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
this._pendingClassArgs = prevClassArgs;
|
|
1063
|
+
this._pendingClassEl = prevClassEl;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1043
1066
|
return elVar;
|
|
1044
1067
|
};
|
|
1045
1068
|
|
|
@@ -1186,7 +1209,13 @@ export function installComponentSupport(CodeGenerator, Lexer) {
|
|
|
1186
1209
|
continue;
|
|
1187
1210
|
}
|
|
1188
1211
|
|
|
1189
|
-
if (
|
|
1212
|
+
if (key === 'innerHTML' || key === 'textContent' || key === 'innerText') {
|
|
1213
|
+
if (this.hasReactiveDeps(value)) {
|
|
1214
|
+
this._setupLines.push(`__effect(() => { ${elVar}.${key} = ${valueCode}; });`);
|
|
1215
|
+
} else {
|
|
1216
|
+
this._createLines.push(`${elVar}.${key} = ${valueCode};`);
|
|
1217
|
+
}
|
|
1218
|
+
} else if (BOOLEAN_ATTRS.has(key)) {
|
|
1190
1219
|
if (this.hasReactiveDeps(value)) {
|
|
1191
1220
|
this._setupLines.push(`__effect(() => { ${elVar}.toggleAttribute('${key}', !!${valueCode}); });`);
|
|
1192
1221
|
} else {
|
package/src/grammar/grammar.rip
CHANGED
|
@@ -73,7 +73,7 @@ grammar =
|
|
|
73
73
|
o 'ReactiveAssign'
|
|
74
74
|
o 'ComputedAssign'
|
|
75
75
|
o 'ReadonlyAssign'
|
|
76
|
-
o '
|
|
76
|
+
o 'Effect'
|
|
77
77
|
o 'If'
|
|
78
78
|
o 'Try'
|
|
79
79
|
o 'While'
|
|
@@ -208,13 +208,13 @@ grammar =
|
|
|
208
208
|
]
|
|
209
209
|
|
|
210
210
|
# Reactive effect (~>) — side effects that run when dependencies change
|
|
211
|
-
|
|
212
|
-
o 'Assignable
|
|
213
|
-
o 'Assignable
|
|
214
|
-
o 'Assignable
|
|
215
|
-
o '
|
|
216
|
-
o '
|
|
217
|
-
o '
|
|
211
|
+
Effect: [
|
|
212
|
+
o 'Assignable EFFECT Expression' , '["effect", 1, 3]'
|
|
213
|
+
o 'Assignable EFFECT TERMINATOR Expression' , '["effect", 1, 4]'
|
|
214
|
+
o 'Assignable EFFECT Block' , '["effect", 1, 3]'
|
|
215
|
+
o 'EFFECT Expression' , '["effect", null, 2]'
|
|
216
|
+
o 'EFFECT TERMINATOR Expression' , '["effect", null, 3]'
|
|
217
|
+
o 'EFFECT Block' , '["effect", null, 2]'
|
|
218
218
|
]
|
|
219
219
|
|
|
220
220
|
# ============================================================================
|
|
@@ -802,7 +802,7 @@ grammar =
|
|
|
802
802
|
o 'EXPORT ReactiveAssign' , '["export", 2]'
|
|
803
803
|
o 'EXPORT ComputedAssign' , '["export", 2]'
|
|
804
804
|
o 'EXPORT ReadonlyAssign' , '["export", 2]'
|
|
805
|
-
o 'EXPORT
|
|
805
|
+
o 'EXPORT Effect' , '["export", 2]'
|
|
806
806
|
o 'EXPORT DEFAULT Expression' , '["export-default", 3]'
|
|
807
807
|
o 'EXPORT DEFAULT INDENT Object OUTDENT' , '["export-default", 4]'
|
|
808
808
|
o 'EXPORT EXPORT_ALL FROM String' , '["export-all", 4]'
|
package/src/grammar/lunar.rip
CHANGED
|
@@ -437,7 +437,7 @@ export install = (Generator) ->
|
|
|
437
437
|
# Statement tokens handled by parseUnary (for 'break if done', 'return x unless err')
|
|
438
438
|
@_exprHandledTokens.add 'STATEMENT'
|
|
439
439
|
@_exprHandledTokens.add 'RETURN'
|
|
440
|
-
@_exprHandledTokens.add '
|
|
440
|
+
@_exprHandledTokens.add 'EFFECT'
|
|
441
441
|
|
|
442
442
|
Generator::_findKeywordTokens = (type, dispatchName, handledTokens, visited) ->
|
|
443
443
|
return if visited.has type.name
|
|
@@ -2271,7 +2271,7 @@ export install = (Generator) ->
|
|
|
2271
2271
|
lines.push " if (token === 'STATEMENT') { const v = tokenText; advance(); return v; }"
|
|
2272
2272
|
lines.push " if (token === 'RETURN') return parseReturn();"
|
|
2273
2273
|
# Fire-and-forget effect (~> expr) — prefix form without left-hand side
|
|
2274
|
-
lines.push " if (token === '
|
|
2274
|
+
lines.push " if (token === 'EFFECT') return parseEffect();"
|
|
2275
2275
|
lines.push " throw new Error('Parse error: unexpected token ' + token + ' at line ' + ((tokenLoc && tokenLoc.r || 0) + 1));"
|
|
2276
2276
|
lines.push "}"
|
|
2277
2277
|
lines.join '\n'
|
package/src/lexer.js
CHANGED
|
@@ -1156,7 +1156,7 @@ export class Lexer {
|
|
|
1156
1156
|
else if (val === '~=') tag = 'COMPUTED_ASSIGN';
|
|
1157
1157
|
else if (val === ':=') tag = 'REACTIVE_ASSIGN';
|
|
1158
1158
|
else if (val === '<=>') tag = 'BIND';
|
|
1159
|
-
else if (val === '~>') tag = '
|
|
1159
|
+
else if (val === '~>') tag = 'EFFECT';
|
|
1160
1160
|
else if (val === '=!') tag = 'READONLY_ASSIGN';
|
|
1161
1161
|
// Merge assignment: *config = {a: 1} → Object.assign(config, {a: 1})
|
|
1162
1162
|
// Also supports *@ = props → Object.assign(this, props)
|