@adriansteffan/reactive 0.0.28 → 0.0.30
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/dist/{mod-CjZm1Ta9.js → mod-BY9yD0Pz.js} +5691 -5512
- package/dist/mod.d.ts +1 -0
- package/dist/reactive.es.js +1 -1
- package/dist/reactive.umd.js +33 -33
- package/dist/style.css +1 -1
- package/dist/{web-B_I1xy51.js → web-Crj4uOnK.js} +1 -1
- package/dist/{web-DfpITFBR.js → web-D4yetCSC.js} +1 -1
- package/package.json +1 -1
- package/src/components/canvasblock.tsx +10 -3
- package/src/components/experimentrunner.tsx +28 -13
- package/src/components/storeui.tsx +327 -0
- package/src/mod.tsx +0 -1
- package/src/utils/bytecode.ts +175 -144
- package/template/src/main.tsx +0 -1
package/dist/style.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! tailwindcss v4.0.9 | MIT License | https://tailwindcss.com */@layer theme{:root,:host{--color-red-100:oklch(.936 .032 17.717);--color-red-200:oklch(.885 .062 18.334);--color-red-500:oklch(.637 .237 25.331);--color-red-600:oklch(.577 .245 27.325);--color-red-700:oklch(.505 .213 27.518);--color-green-500:oklch(.723 .219 149.579);--color-green-600:oklch(.627 .194 149.214);--color-green-700:oklch(.527 .154 150.069);--color-blue-400:oklch(.707 .165 254.624);--color-blue-500:oklch(.623 .214 259.815);--color-blue-600:oklch(.546 .245 262.881);--color-blue-700:oklch(.488 .243 264.376);--color-indigo-500:oklch(.585 .233 277.117);--color-indigo-600:oklch(.511 .262 276.966);--color-indigo-700:oklch(.457 .24 277.023);--color-gray-50:oklch(.985 .002 247.839);--color-gray-100:oklch(.967 .003 264.542);--color-gray-200:oklch(.928 .006 264.531);--color-gray-300:oklch(.872 .01 258.338);--color-gray-500:oklch(.551 .027 264.364);--color-gray-600:oklch(.446 .03 256.802);--color-gray-700:oklch(.373 .034 259.733);--color-gray-800:oklch(.278 .033 256.848);--color-gray-900:oklch(.21 .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-2xl:42rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5/2.25);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-font-feature-settings:var(--font-sans--font-feature-settings);--default-font-variation-settings:var(--font-sans--font-variation-settings);--default-mono-font-family:var(--font-mono);--default-mono-font-feature-settings:var(--font-mono--font-feature-settings);--default-mono-font-variation-settings:var(--font-mono--font-variation-settings);--font-sans:Atkinson Hyperlegible,sans-serif;--animate-slide-down:slideDown .8s ease-out forwards;--animate-fade-in:fadeIn .5s ease-out forwards}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}body{line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::-moz-placeholder{opacity:1;color:color-mix(in oklab,currentColor 50%,transparent)}::placeholder{opacity:1;color:color-mix(in oklab,currentColor 50%,transparent)}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*,:after,:before,::backdrop{border-color:var(--color-gray-200,currentColor)}::file-selector-button{border-color:var(--color-gray-200,currentColor)}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.visible\!{visibility:visible!important}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing)*0)}.-z-10{z-index:-10}.z-50{z-index:50}.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-6{grid-column:span 6/span 6}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.me-4{margin-inline-end:calc(var(--spacing)*4)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-8{margin-top:calc(var(--spacing)*8)}.mt-16{margin-top:calc(var(--spacing)*16)}.mt-20{margin-top:calc(var(--spacing)*20)}.mt-auto{margin-top:auto}.mr-2{margin-right:calc(var(--spacing)*2)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.mb-20{margin-bottom:calc(var(--spacing)*20)}.-ml-1{margin-left:calc(var(--spacing)*-1)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-3{margin-left:calc(var(--spacing)*3)}.ml-4{margin-left:calc(var(--spacing)*4)}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-8{height:calc(var(--spacing)*8)}.h-12{height:calc(var(--spacing)*12)}.h-full{height:100%}.max-h-60{max-height:calc(var(--spacing)*60)}.min-h-10{min-height:calc(var(--spacing)*10)}.w-3{width:calc(var(--spacing)*3)}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-8{width:calc(var(--spacing)*8)}.w-11{width:calc(var(--spacing)*11)}.w-12{width:calc(var(--spacing)*12)}.w-64{width:calc(var(--spacing)*64)}.w-258{width:calc(var(--spacing)*258)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-md{max-width:var(--container-md)}.max-w-prose{max-width:65ch}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.flex-grow{flex-grow:1}.translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-1{--tw-translate-x:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-5{--tw-translate-x:calc(var(--spacing)*5);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-y-1{--tw-translate-y:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.rotate-180{rotate:180deg}.transform{transform:var(--tw-rotate-x)var(--tw-rotate-y)var(--tw-rotate-z)var(--tw-skew-x)var(--tw-skew-y)}.animate-fade-in{animation:var(--animate-fade-in)}.animate-pulse{animation:var(--animate-pulse)}.animate-slide-down{animation:var(--animate-slide-down)}.animate-spin{animation:var(--animate-spin)}.cursor-pointer{cursor:pointer}.resize{resize:both}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-6{gap:calc(var(--spacing)*6)}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*2)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*4)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px*var(--tw-divide-y-reverse));border-bottom-width:calc(1px*calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-gray-200>:not(:last-child)){border-color:var(--color-gray-200)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-none{border-radius:0}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-4{border-style:var(--tw-border-style);border-width:4px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-black{border-color:var(--color-black)}.border-blue-500{border-color:var(--color-blue-500)}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-transparent{border-color:#0000}.border-t-transparent{border-top-color:#0000}.bg-black{background-color:var(--color-black)}.bg-blue-500{background-color:var(--color-blue-500)}.bg-blue-600{background-color:var(--color-blue-600)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-200{background-color:var(--color-gray-200)}.bg-green-600{background-color:var(--color-green-600)}.bg-indigo-600{background-color:var(--color-indigo-600)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-500{background-color:var(--color-red-500)}.bg-red-600{background-color:var(--color-red-600)}.bg-white{background-color:var(--color-white)}.bg-\[radial-gradient\(\#e5e7eb_1px\,transparent_1px\)\]{background-image:radial-gradient(#e5e7eb 1px,#0000 1px)}.\[background-size\:16px_16px\]{background-size:16px 16px}.p-2{padding:calc(var(--spacing)*2)}.p-4{padding:calc(var(--spacing)*4)}.p-5{padding:calc(var(--spacing)*5)}.p-6{padding:calc(var(--spacing)*6)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-5{padding-inline:calc(var(--spacing)*5)}.px-8{padding-inline:calc(var(--spacing)*8)}.px-10{padding-inline:calc(var(--spacing)*10)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.py-5{padding-block:calc(var(--spacing)*5)}.pt-2{padding-top:calc(var(--spacing)*2)}.pr-10{padding-right:calc(var(--spacing)*10)}.pl-3{padding-left:calc(var(--spacing)*3)}.text-center{text-align:center}.text-left{text-align:left}.font-sans{font-family:var(--font-sans)}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-black{color:var(--color-black)}.text-blue-400{color:var(--color-blue-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-indigo-600{color:var(--color-indigo-600)}.text-red-500{color:var(--color-red-500)}.text-red-700{color:var(--color-red-700)}.text-white{color:var(--color-white)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.underline{text-decoration-line:underline}.opacity-0{opacity:0}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[2px_2px_0px_0px_rgba\(0\,0\,0\,1\)\]{--tw-shadow:2px 2px 0px 0px var(--tw-shadow-color,#000);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[2px_2px_0px_rgba\(0\,0\,0\,1\)\]{--tw-shadow:2px 2px 0px var(--tw-shadow-color,#000);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-0{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(0px + var(--tw-ring-offset-width))var(--tw-ring-color,currentColor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media (hover:hover){.hover\:translate-x-\[2px\]:hover{--tw-translate-x:2px;translate:var(--tw-translate-x)var(--tw-translate-y)}.hover\:translate-y-\[2px\]:hover{--tw-translate-y:2px;translate:var(--tw-translate-x)var(--tw-translate-y)}.hover\:bg-black:hover{background-color:var(--color-black)}.hover\:bg-blue-600:hover{background-color:var(--color-blue-600)}.hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.hover\:bg-gray-50:hover{background-color:var(--color-gray-50)}.hover\:bg-green-700:hover{background-color:var(--color-green-700)}.hover\:bg-indigo-700:hover{background-color:var(--color-indigo-700)}.hover\:bg-red-200:hover{background-color:var(--color-red-200)}.hover\:bg-red-600:hover{background-color:var(--color-red-600)}.hover\:text-white:hover{color:var(--color-white)}.hover\:shadow-\[1px_1px_0px_0px_rgba\(0\,0\,0\,1\)\]:hover{--tw-shadow:1px 1px 0px 0px var(--tw-shadow-color,#000);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-none:hover{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.focus\:border-blue-500:focus{border-color:var(--color-blue-500)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentColor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-blue-500:focus{--tw-ring-color:var(--color-blue-500)}.focus\:ring-green-500:focus{--tw-ring-color:var(--color-green-500)}.focus\:ring-indigo-500:focus{--tw-ring-color:var(--color-indigo-500)}.focus\:ring-red-500:focus{--tw-ring-color:var(--color-red-500)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.active\:translate-x-1:active{--tw-translate-x:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.active\:translate-y-1:active{--tw-translate-y:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.active\:shadow-none:active{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media (width>=40rem){.sm\:mt-12{margin-top:calc(var(--spacing)*12)}.sm\:rounded-md{border-radius:var(--radius-md)}.sm\:px-6{padding-inline:calc(var(--spacing)*6)}.sm\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}}@media (width>=48rem){.md\:flex-row{flex-direction:row}}}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}:root{--font-atkinson:"Atkinson Hyperlegible",system-ui,sans-serif}*{-webkit-touch-callout:none}@font-face{font-family:Atkinson Hyperlegible;src:url(/Atkinson_Hyperlegible/AtkinsonHyperlegible-Regular.ttf)format("truetype");font-weight:400;font-style:normal}@font-face{font-family:Atkinson Hyperlegible;src:url(/Atkinson_Hyperlegible/AtkinsonHyperlegible-Bold.ttf)format("truetype");font-weight:700;font-style:normal}@font-face{font-family:Atkinson Hyperlegible;src:url(/Atkinson_Hyperlegible/AtkinsonHyperlegible-Italic.ttf)format("truetype");font-weight:400;font-style:italic}@font-face{font-family:Atkinson Hyperlegible;src:url(/Atkinson_Hyperlegible/AtkinsonHyperlegible-BoldItalic.ttf)format("truetype");font-weight:700;font-style:italic}.sd-root-modern{background-image:radial-gradient(#e5e7eb 1px,#0000 1px);background-size:16px 16px;font-family:Atkinson Hyperlegible,sans-serif;position:relative;background-color:#fff!important}.sd-root-modern *{font-family:Atkinson Hyperlegible,sans-serif}.sd-row,.sd-clearfix{padding-bottom:20px!important}@media (width<=639px){.sd-imagepicker__item--inline{margin-top:10px;margin-left:auto;margin-right:auto}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false;initial-value:rotateX(0)}@property --tw-rotate-y{syntax:"*";inherits:false;initial-value:rotateY(0)}@property --tw-rotate-z{syntax:"*";inherits:false;initial-value:rotateZ(0)}@property --tw-skew-x{syntax:"*";inherits:false;initial-value:skewX(0)}@property --tw-skew-y{syntax:"*";inherits:false;initial-value:skewY(0)}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}/*!
|
|
1
|
+
/*!
|
|
2
2
|
* surveyjs - Survey JavaScript library v1.12.11
|
|
3
3
|
* Copyright (c) 2015-2024 Devsoft Baltic OÜ - http://surveyjs.io/
|
|
4
4
|
* License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
package/package.json
CHANGED
|
@@ -21,7 +21,9 @@ interface CanvasSlide {
|
|
|
21
21
|
hideOnResponse?: boolean;
|
|
22
22
|
ignoreData?: boolean;
|
|
23
23
|
allowedKeys?: string[] | boolean;
|
|
24
|
-
metadata?:
|
|
24
|
+
metadata?:
|
|
25
|
+
| Record<string, any>
|
|
26
|
+
| ((data?: RefinedTrialData[], store?: Store) => Record<string, any>);
|
|
25
27
|
nestMetadata?: boolean;
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -324,9 +326,14 @@ export default function CanvasBlock({
|
|
|
324
326
|
reactionTime: responseData ? responseData.reactionTime : null,
|
|
325
327
|
} as CanvasResultData;
|
|
326
328
|
|
|
329
|
+
const metadata =
|
|
330
|
+
typeof slide.metadata === 'function'
|
|
331
|
+
? slide.metadata(dataRef.current, storeRef.current)
|
|
332
|
+
: slide.metadata;
|
|
333
|
+
|
|
327
334
|
if (slide.nestMetadata) {
|
|
328
|
-
trialData = { ...trialData, metadata:
|
|
329
|
-
} else trialData = { ...
|
|
335
|
+
trialData = { ...trialData, metadata: metadata };
|
|
336
|
+
} else trialData = { ...metadata, ...trialData };
|
|
330
337
|
|
|
331
338
|
dataRef.current.push(trialData);
|
|
332
339
|
}
|
|
@@ -23,6 +23,7 @@ import CheckDevice from './checkdevice';
|
|
|
23
23
|
|
|
24
24
|
import VoicerecorderQuestionComponent from './voicerecorder';
|
|
25
25
|
import React from 'react';
|
|
26
|
+
import StoreUI from './storeui';
|
|
26
27
|
|
|
27
28
|
type ComponentsMap = {
|
|
28
29
|
[key: string]: ComponentType<any>;
|
|
@@ -40,6 +41,7 @@ const defaultComponents: ComponentsMap = {
|
|
|
40
41
|
RequestFilePermission,
|
|
41
42
|
CanvasBlock,
|
|
42
43
|
CheckDevice,
|
|
44
|
+
StoreUI
|
|
43
45
|
};
|
|
44
46
|
|
|
45
47
|
const defaultCustomQuestions: ComponentsMap = {
|
|
@@ -51,7 +53,11 @@ interface RuntimeComponentContent {
|
|
|
51
53
|
type: string;
|
|
52
54
|
collectRefreshRate?: boolean;
|
|
53
55
|
hideSettings?: string[] | boolean;
|
|
54
|
-
|
|
56
|
+
metadata?:
|
|
57
|
+
| Record<string, any>
|
|
58
|
+
| ((data: RefinedTrialData[], store: Store) => Record<string, any>);
|
|
59
|
+
nestMetadata?: boolean;
|
|
60
|
+
props?: Record<string, any> | ((data: RefinedTrialData[], store: Store) => Record<string, any>);
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
function isRuntimeComponentContent(content: any): content is RuntimeComponentContent {
|
|
@@ -76,7 +82,7 @@ export default function ExperimentRunner({
|
|
|
76
82
|
}, [timeline]);
|
|
77
83
|
|
|
78
84
|
const [instructionPointer, setInstructionPointer] = useState(0);
|
|
79
|
-
const
|
|
85
|
+
const dataRef = useRef<RefinedTrialData[]>((() => {
|
|
80
86
|
const urlParams: Record<string, any> = {};
|
|
81
87
|
const searchParams = new URLSearchParams(window.location.search);
|
|
82
88
|
for (const [key, value] of searchParams.entries()) {
|
|
@@ -101,8 +107,7 @@ export default function ExperimentRunner({
|
|
|
101
107
|
// Then add/overwrite with registry entries
|
|
102
108
|
for (const param of registry) {
|
|
103
109
|
params[param.name] = {
|
|
104
|
-
value:
|
|
105
|
-
param.value !== undefined ? param.value : urlParams[param.name],
|
|
110
|
+
value: param.value !== undefined ? param.value : urlParams[param.name],
|
|
106
111
|
registered: true,
|
|
107
112
|
defaultValue: param.defaultValue,
|
|
108
113
|
type: param.type,
|
|
@@ -123,9 +128,9 @@ export default function ExperimentRunner({
|
|
|
123
128
|
params,
|
|
124
129
|
},
|
|
125
130
|
};
|
|
126
|
-
|
|
131
|
+
|
|
127
132
|
return [initialData];
|
|
128
|
-
});
|
|
133
|
+
})());
|
|
129
134
|
|
|
130
135
|
const [totalTrialsCompleted, setTotalTrialsCompleted] = useState(0);
|
|
131
136
|
const lastTrialEndTimeRef = useRef(now());
|
|
@@ -163,7 +168,7 @@ export default function ExperimentRunner({
|
|
|
163
168
|
if (currentInstruction?.type === 'ExecuteContent') {
|
|
164
169
|
const content = currentInstruction.content;
|
|
165
170
|
if (isRuntimeComponentContent(content)) {
|
|
166
|
-
|
|
171
|
+
let trialData: ComponentResultData = {
|
|
167
172
|
index: instructionPointer,
|
|
168
173
|
trialNumber: totalTrialsCompleted + 1,
|
|
169
174
|
start: startTime,
|
|
@@ -173,7 +178,17 @@ export default function ExperimentRunner({
|
|
|
173
178
|
name: content.name ?? '',
|
|
174
179
|
responseData: componentResponseData,
|
|
175
180
|
};
|
|
176
|
-
|
|
181
|
+
|
|
182
|
+
const metadata =
|
|
183
|
+
typeof content.metadata === 'function'
|
|
184
|
+
? content.metadata(dataRef.current, experimentStoreRef.current)
|
|
185
|
+
: content.metadata;
|
|
186
|
+
|
|
187
|
+
if (content.nestMetadata) {
|
|
188
|
+
trialData = { ...trialData, metadata: metadata };
|
|
189
|
+
} else trialData = { ...metadata, ...trialData };
|
|
190
|
+
|
|
191
|
+
dataRef.current = [...dataRef.current, trialData];
|
|
177
192
|
setTotalTrialsCompleted((prevCount) => prevCount + 1);
|
|
178
193
|
} else {
|
|
179
194
|
console.log(
|
|
@@ -191,7 +206,7 @@ export default function ExperimentRunner({
|
|
|
191
206
|
|
|
192
207
|
switch (nextInstruction.type) {
|
|
193
208
|
case 'IfGoto':
|
|
194
|
-
if (nextInstruction.cond(experimentStoreRef.current,
|
|
209
|
+
if (nextInstruction.cond(experimentStoreRef.current, dataRef.current)) {
|
|
195
210
|
const markerIndex = trialByteCode.markers[nextInstruction.marker];
|
|
196
211
|
if (markerIndex !== undefined) {
|
|
197
212
|
nextPointer = markerIndex;
|
|
@@ -206,7 +221,7 @@ export default function ExperimentRunner({
|
|
|
206
221
|
break;
|
|
207
222
|
|
|
208
223
|
case 'UpdateStore':
|
|
209
|
-
updateStore(nextInstruction.fun(experimentStoreRef.current,
|
|
224
|
+
updateStore(nextInstruction.fun(experimentStoreRef.current, dataRef.current));
|
|
210
225
|
nextPointer++;
|
|
211
226
|
break;
|
|
212
227
|
|
|
@@ -334,7 +349,7 @@ export default function ExperimentRunner({
|
|
|
334
349
|
if (Component) {
|
|
335
350
|
const componentProps =
|
|
336
351
|
typeof content.props === 'function'
|
|
337
|
-
? content.props(
|
|
352
|
+
? content.props(dataRef.current, experimentStoreRef.current)
|
|
338
353
|
: content.props || {};
|
|
339
354
|
|
|
340
355
|
componentToRender = (
|
|
@@ -343,7 +358,7 @@ export default function ExperimentRunner({
|
|
|
343
358
|
next={next}
|
|
344
359
|
updateStore={updateStore}
|
|
345
360
|
store={experimentStoreRef.current}
|
|
346
|
-
data={
|
|
361
|
+
data={dataRef.current}
|
|
347
362
|
{...(content.type === 'Quest' ? { customQuestions: customQuestionsMap } : {})}
|
|
348
363
|
{...componentProps}
|
|
349
364
|
/>
|
|
@@ -362,7 +377,7 @@ export default function ExperimentRunner({
|
|
|
362
377
|
}
|
|
363
378
|
|
|
364
379
|
return (
|
|
365
|
-
<div className='w-full'>
|
|
380
|
+
<div className='w-full h-full'>
|
|
366
381
|
<div
|
|
367
382
|
className={` ${
|
|
368
383
|
config.showProgressBar ? '' : 'hidden '
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { BaseComponentProps } from '../mod';
|
|
2
|
+
import React, { useState, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
interface BaseFieldConfig {
|
|
5
|
+
type: 'string' | 'integer' | 'float' | 'boolean';
|
|
6
|
+
storeKey: string;
|
|
7
|
+
label: string;
|
|
8
|
+
prompt?: string;
|
|
9
|
+
validate?: (value: any) => boolean | string;
|
|
10
|
+
defaultValue?: any;
|
|
11
|
+
component?: React.ComponentType<{ value: any }>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface StringFieldConfig extends BaseFieldConfig {
|
|
15
|
+
type: 'string';
|
|
16
|
+
minLength?: number;
|
|
17
|
+
maxLength?: number;
|
|
18
|
+
defaultValue?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface NumberFieldConfig extends BaseFieldConfig {
|
|
22
|
+
type: 'integer' | 'float';
|
|
23
|
+
min?: number;
|
|
24
|
+
max?: number;
|
|
25
|
+
step?: number;
|
|
26
|
+
defaultValue?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface BooleanFieldConfig extends BaseFieldConfig {
|
|
30
|
+
type: 'boolean';
|
|
31
|
+
defaultValue?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type FieldConfig = StringFieldConfig | NumberFieldConfig | BooleanFieldConfig;
|
|
35
|
+
|
|
36
|
+
interface StoreUIProps extends BaseComponentProps {
|
|
37
|
+
title?: string;
|
|
38
|
+
description?: string;
|
|
39
|
+
fields: FieldConfig[];
|
|
40
|
+
saveButtonText?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function StoreUI({
|
|
44
|
+
next,
|
|
45
|
+
updateStore,
|
|
46
|
+
store,
|
|
47
|
+
title = 'Settings',
|
|
48
|
+
description,
|
|
49
|
+
fields,
|
|
50
|
+
saveButtonText = 'Save Configuration',
|
|
51
|
+
}: StoreUIProps) {
|
|
52
|
+
|
|
53
|
+
const [values, setValues] = useState<Record<string, any>>({});
|
|
54
|
+
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
55
|
+
const [touched, setTouched] = useState<Record<string, boolean>>({});
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const initialValues: Record<string, any> = {};
|
|
60
|
+
|
|
61
|
+
fields.forEach(field => {
|
|
62
|
+
|
|
63
|
+
initialValues[field.storeKey] =
|
|
64
|
+
store?.[field.storeKey] !== undefined
|
|
65
|
+
? store[field.storeKey]
|
|
66
|
+
: field.defaultValue;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
setValues(initialValues);
|
|
70
|
+
|
|
71
|
+
const initialTouched: Record<string, boolean> = {};
|
|
72
|
+
fields.forEach(field => {
|
|
73
|
+
initialTouched[field.storeKey] = false;
|
|
74
|
+
});
|
|
75
|
+
setTouched(initialTouched);
|
|
76
|
+
}, [store, fields]);
|
|
77
|
+
|
|
78
|
+
const validateField = (field: FieldConfig, value: any): string | null => {
|
|
79
|
+
if (field.type === 'integer' || field.type === 'float') {
|
|
80
|
+
if (value === undefined || value === null || value === '') {
|
|
81
|
+
return 'This field is required';
|
|
82
|
+
} else if (field.min !== undefined && value < field.min) {
|
|
83
|
+
return `Value must be at least ${field.min}`;
|
|
84
|
+
} else if (field.max !== undefined && value > field.max) {
|
|
85
|
+
return `Value must be at most ${field.max}`;
|
|
86
|
+
}
|
|
87
|
+
} else if (field.type === 'string') {
|
|
88
|
+
if (field.minLength !== undefined && value.length < field.minLength) {
|
|
89
|
+
return `Must be at least ${field.minLength} characters`;
|
|
90
|
+
} else if (field.maxLength !== undefined && value.length > field.maxLength) {
|
|
91
|
+
return `Must be at most ${field.maxLength} characters`;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (field.validate) {
|
|
96
|
+
const validationResult = field.validate(value);
|
|
97
|
+
if (validationResult !== true && validationResult !== '') {
|
|
98
|
+
return typeof validationResult === 'string'
|
|
99
|
+
? validationResult
|
|
100
|
+
: 'Invalid value';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return null;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
const newErrors: Record<string, string> = {};
|
|
109
|
+
|
|
110
|
+
fields.forEach(field => {
|
|
111
|
+
const value = values[field.storeKey];
|
|
112
|
+
|
|
113
|
+
if (!touched[field.storeKey]) return;
|
|
114
|
+
|
|
115
|
+
const error = validateField(field, value);
|
|
116
|
+
if (error) {
|
|
117
|
+
newErrors[field.storeKey] = error;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
setErrors(newErrors);
|
|
122
|
+
}, [values, fields, touched]);
|
|
123
|
+
|
|
124
|
+
const handleChange = (storeKey: string, value: any) => {
|
|
125
|
+
setValues(prev => ({
|
|
126
|
+
...prev,
|
|
127
|
+
[storeKey]: value
|
|
128
|
+
}));
|
|
129
|
+
|
|
130
|
+
setTouched(prev => ({
|
|
131
|
+
...prev,
|
|
132
|
+
[storeKey]: true
|
|
133
|
+
}));
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const handleSave = () => {
|
|
137
|
+
const allTouched: Record<string, boolean> = {};
|
|
138
|
+
fields.forEach(field => {
|
|
139
|
+
allTouched[field.storeKey] = true;
|
|
140
|
+
});
|
|
141
|
+
setTouched(allTouched);
|
|
142
|
+
|
|
143
|
+
const newErrors: Record<string, string> = {};
|
|
144
|
+
fields.forEach(field => {
|
|
145
|
+
const value = values[field.storeKey];
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
if (field.type === 'integer' || field.type === 'float') {
|
|
149
|
+
if (value === undefined || value === null || value === '') {
|
|
150
|
+
newErrors[field.storeKey] = 'This field is required';
|
|
151
|
+
} else if (field.min !== undefined && value < field.min) {
|
|
152
|
+
newErrors[field.storeKey] = `Value must be at least ${field.min}`;
|
|
153
|
+
} else if (field.max !== undefined && value > field.max) {
|
|
154
|
+
newErrors[field.storeKey] = `Value must be at most ${field.max}`;
|
|
155
|
+
}
|
|
156
|
+
} else if (field.type === 'string') {
|
|
157
|
+
if (field.minLength !== undefined && value.length < field.minLength) {
|
|
158
|
+
newErrors[field.storeKey] = `Must be at least ${field.minLength} characters`;
|
|
159
|
+
} else if (field.maxLength !== undefined && value.length > field.maxLength) {
|
|
160
|
+
newErrors[field.storeKey] = `Must be at most ${field.maxLength} characters`;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (field.validate && !newErrors[field.storeKey]) {
|
|
165
|
+
const validationResult = field.validate(value);
|
|
166
|
+
if (validationResult !== true && validationResult !== '') {
|
|
167
|
+
newErrors[field.storeKey] = typeof validationResult === 'string'
|
|
168
|
+
? validationResult
|
|
169
|
+
: 'Invalid value';
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
setErrors(newErrors);
|
|
175
|
+
|
|
176
|
+
if (Object.keys(newErrors).length === 0) {
|
|
177
|
+
updateStore(values);
|
|
178
|
+
next(values);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const hasErrors = Object.keys(errors).length > 0;
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<div className="max-w-prose mx-auto mt-20 mb-20 px-4">
|
|
186
|
+
<h1 className="text-4xl font-bold mb-6">{title}</h1>
|
|
187
|
+
|
|
188
|
+
{description && (
|
|
189
|
+
<article className="prose prose-lg mb-10">
|
|
190
|
+
{description}
|
|
191
|
+
</article>
|
|
192
|
+
)}
|
|
193
|
+
|
|
194
|
+
<div className="mt-12 space-y-12">
|
|
195
|
+
{fields.map((field) => (
|
|
196
|
+
<div key={field.storeKey} className="space-y-2">
|
|
197
|
+
<label className="block text-xl font-medium text-gray-900">
|
|
198
|
+
{field.label}
|
|
199
|
+
{field.type === 'integer' && field.min !== undefined && field.max !== undefined &&
|
|
200
|
+
` (${field.min}-${field.max})`}
|
|
201
|
+
{field.type === 'float' && field.min !== undefined && field.max !== undefined &&
|
|
202
|
+
` (${field.min}-${field.max})`}
|
|
203
|
+
</label>
|
|
204
|
+
|
|
205
|
+
{field.prompt && (
|
|
206
|
+
<p className="mb-4 text-base text-gray-700 mb-2">
|
|
207
|
+
{field.prompt}
|
|
208
|
+
</p>
|
|
209
|
+
)}
|
|
210
|
+
|
|
211
|
+
{field.type === 'boolean' && (
|
|
212
|
+
<div className="flex items-center">
|
|
213
|
+
<div
|
|
214
|
+
className={`relative cursor-pointer w-10 h-10 flex items-center justify-center border-2 border-black rounded-xl shadow-[2px_2px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-none ${values[field.storeKey] ? 'bg-blue-600 text-white' : 'bg-white'}`}
|
|
215
|
+
onClick={() => handleChange(field.storeKey, !values[field.storeKey])}
|
|
216
|
+
>
|
|
217
|
+
<input
|
|
218
|
+
type="checkbox"
|
|
219
|
+
checked={!!values[field.storeKey]}
|
|
220
|
+
onChange={(e) => handleChange(field.storeKey, e.target.checked)}
|
|
221
|
+
className="absolute opacity-0 h-0 w-0"
|
|
222
|
+
/>
|
|
223
|
+
{values[field.storeKey] && (
|
|
224
|
+
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
225
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
|
|
226
|
+
</svg>
|
|
227
|
+
)}
|
|
228
|
+
</div>
|
|
229
|
+
<span className="ml-3 text-lg">Enabled</span>
|
|
230
|
+
</div>
|
|
231
|
+
)}
|
|
232
|
+
|
|
233
|
+
{(field.type === 'integer' || field.type === 'float') && (
|
|
234
|
+
<div className="flex items-center space-x-4">
|
|
235
|
+
{field.min !== undefined && field.max !== undefined && (
|
|
236
|
+
<button
|
|
237
|
+
onClick={() => handleChange(
|
|
238
|
+
field.storeKey,
|
|
239
|
+
Math.max(
|
|
240
|
+
field.min || 0,
|
|
241
|
+
(values[field.storeKey] || 0) - (field.step || 1)
|
|
242
|
+
)
|
|
243
|
+
)}
|
|
244
|
+
className="cursor-pointer bg-white h-10 w-10 flex items-center justify-center border-2 border-black font-bold text-black text-lg rounded-full shadow-[2px_2px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-none"
|
|
245
|
+
>
|
|
246
|
+
-
|
|
247
|
+
</button>
|
|
248
|
+
)}
|
|
249
|
+
|
|
250
|
+
<input
|
|
251
|
+
type='text'
|
|
252
|
+
value={values[field.storeKey] || ''}
|
|
253
|
+
onChange={(e) => {
|
|
254
|
+
const rawValue = e.target.value;
|
|
255
|
+
let parsedValue;
|
|
256
|
+
|
|
257
|
+
if (field.type === 'integer') {
|
|
258
|
+
parsedValue = rawValue === '' ? '' : parseInt(rawValue, 10);
|
|
259
|
+
} else if (field.type === 'float') {
|
|
260
|
+
parsedValue = rawValue === '' ? '' : parseFloat(rawValue);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (rawValue === '' || !isNaN(parsedValue as number)) {
|
|
264
|
+
handleChange(field.storeKey, parsedValue);
|
|
265
|
+
}
|
|
266
|
+
}}
|
|
267
|
+
step={field.step || (field.type === 'integer' ? 1 : 0.1)}
|
|
268
|
+
min={field.min}
|
|
269
|
+
max={field.max}
|
|
270
|
+
className="w-24 px-4 py-2 text-center border-2 border-black rounded-xl text-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
271
|
+
/>
|
|
272
|
+
|
|
273
|
+
{field.min !== undefined && field.max !== undefined && (
|
|
274
|
+
<button
|
|
275
|
+
onClick={() => handleChange(
|
|
276
|
+
field.storeKey,
|
|
277
|
+
Math.min(
|
|
278
|
+
field.max || Infinity,
|
|
279
|
+
(values[field.storeKey] || 0) + (field.step || 1)
|
|
280
|
+
)
|
|
281
|
+
)}
|
|
282
|
+
className="cursor-pointer bg-white h-10 w-10 flex items-center justify-center border-2 border-black font-bold text-black text-lg rounded-full shadow-[2px_2px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-none"
|
|
283
|
+
>
|
|
284
|
+
+
|
|
285
|
+
</button>
|
|
286
|
+
)}
|
|
287
|
+
</div>
|
|
288
|
+
)}
|
|
289
|
+
|
|
290
|
+
{field.type === 'string' && (
|
|
291
|
+
<input
|
|
292
|
+
type="text"
|
|
293
|
+
value={values[field.storeKey] || ''}
|
|
294
|
+
onChange={(e) => handleChange(field.storeKey, e.target.value)}
|
|
295
|
+
className="w-full px-4 py-2 border-2 border-black rounded-xl text-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
296
|
+
/>
|
|
297
|
+
)}
|
|
298
|
+
|
|
299
|
+
{errors[field.storeKey] && touched[field.storeKey] && (
|
|
300
|
+
<div className="text-red-500 text-sm mt-1">
|
|
301
|
+
{errors[field.storeKey]}
|
|
302
|
+
</div>
|
|
303
|
+
)}
|
|
304
|
+
|
|
305
|
+
{field.component && (
|
|
306
|
+
<div className="mt-4">
|
|
307
|
+
{React.createElement(field.component, { value: values[field.storeKey] })}
|
|
308
|
+
</div>
|
|
309
|
+
)}
|
|
310
|
+
</div>
|
|
311
|
+
))}
|
|
312
|
+
</div>
|
|
313
|
+
|
|
314
|
+
<div className="mt-12 flex justify-center">
|
|
315
|
+
<button
|
|
316
|
+
onClick={handleSave}
|
|
317
|
+
disabled={hasErrors}
|
|
318
|
+
className={`bg-white cursor-pointer px-8 py-3 border-2 border-black font-bold text-black text-lg rounded-xl shadow-[2px_2px_0px_rgba(0,0,0,1)] hover:translate-x-[2px] hover:translate-y-[2px] hover:shadow-none ${hasErrors ? 'opacity-50 cursor-not-allowed' : ''}`}
|
|
319
|
+
>
|
|
320
|
+
{saveButtonText}
|
|
321
|
+
</button>
|
|
322
|
+
</div>
|
|
323
|
+
</div>
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export default StoreUI;
|
package/src/mod.tsx
CHANGED