Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: playground: generator configs #1449

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/docs/src/components/code-rotator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,11 @@ export const CodeRotator = component$((props: { class: ClassList }) => {
const compileAll = $(async (code: string) => {
await Promise.allSettled(
frameworkExamples.map(async (framework) => {
const output = await compile(code, framework as OutputFramework, 'jsx');
const output = await compile({
code,
output: framework as OutputFramework,
inputSyntax: 'jsx'
});
(outputs as any)[framework] = output.replace(
/\n?\n?import { useStore } from "..";\n?/g,
'',
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default component$(() => {
alt="Mitosis logo"
class="object-contain max-md:max-w-[110px]"
width={160}
height={80}
height={40}
src="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F0fdb9aabd10f4205b3b3b56d7b950239"
/>
</Link>
Expand Down
113 changes: 113 additions & 0 deletions packages/docs/src/components/options-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { ToReactOptions, ToVueOptions } from '@builder.io/mitosis';
import { $, Signal, component$, useSignal } from '@builder.io/qwik';
import { OutputFramework } from '~/services/compile';
import Select from './select';

type Option<O> = {name: keyof O} & ({
type: 'boolean';
default: boolean
} | {
type: 'enum';
enum: Array<string>;
default: string;
});

const DEFAULT_OPTIONS: Option<ToVueOptions>[] = [{
name: 'typescript',
type: 'boolean',
default: true
}]

type Dictionary<T> = {
[index: string]: T
}
type Options = Dictionary<string | boolean>

export const getDefaultOptions = (target: OutputFramework) => {
const opts = getOptions(target)
const opt: Options = {}

for (const o of opts) {
opt[o.name] = o.default
}

return opt
}


const _getOptions = (target:OutputFramework) => {
switch (target) {
case 'vue':{
const o: Array<Option<ToVueOptions>> = [{
name: 'casing',
type: 'enum',
enum: ['pascal', 'kebab'],
default: 'pascal'
},
{
name: 'api',
type: 'enum',
enum: ['options', 'composition'],
default: 'composition'
}]

return o}

case 'react': {

const o: Array<Option<ToReactOptions>> = [{
name: 'stylesType',
type: 'enum',
enum: [
'emotion', 'styled-components', 'styled-jsx', 'react-native', 'style-tag'
],
default: 'style-tag'
}]
return o }
default:
return []
}
}
const getOptions = (target:OutputFramework) => {
return [
...DEFAULT_OPTIONS,
..._getOptions(target)
]
}

export default component$(
({options, target}: { options: Options, target:Signal<OutputFramework> }) => {
const showModal = useSignal(false);


return (
<div>
<button onClick$={$(() => {
showModal.value = !showModal.value
})} class={'px-3 py-1.5 outline-0 rounded text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-purple-990 bg-primary focus:ring-primary bg-opacity-10 border border-primary border-opacity-50 transition-colors duration-200 ease-in-out appearance-none'}>options</button>

{showModal.value && <div class={"absolute top-40 bottom-40 left-40 right-40 bg-purple-990 z-50 p-10 max-w-2xl"}>
<h1>{target.value} settings.</h1>

<div class="w-full flex flex-col gap-4 pt-4">
{getOptions(target.value).map(option => {
console.log('consuming option: ', {option, options});

return <div class="flex gap-2 items-baseline justify-between">
<div>{option.name}</div>
<div>{option.type === 'boolean' ? <form><input type="checkbox" checked={options[option.name] as boolean} onChange$={$(() => {
console.log('before: ', options.value);

options[option.name] = !options[option.name]

console.log('after: ', options.value);
})} ></input></form> : <Select onChange$={$((newVal) => {
options[option.name] = newVal
})} value={options[option.name] as string} options={option.enum} class="" />}</div>
</div>
})}
</div></div>}
</div>
);
},
);
92 changes: 59 additions & 33 deletions packages/docs/src/routes/playground/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { $, component$, useSignal, useVisibleTask$ } from '@builder.io/qwik';
import { $, component$, useSignal, useStore, useVisibleTask$ } from '@builder.io/qwik';
import { DocumentHead, routeLoader$, useLocation, useNavigate } from '@builder.io/qwik-city';
import lzString from 'lz-string';
import { ContentLoaderCode } from 'qwik-content-loader';
import { CodeEditor } from '~/components/code-editor';
import OptionsModal, { getDefaultOptions } from '~/components/options-modal';
import Select from '~/components/select';
import {
CompileArgs,
InputSyntax,
OutputFramework,
compile,
Expand Down Expand Up @@ -39,9 +41,9 @@ const useOutput1 = routeLoader$(async (requestEvent) => {
}

const output = await compile(
code || defaultCode,
outputTab || defaultTopTab,
inputTab || defaultInputTab,
{code: code || defaultCode,
output: outputTab || defaultTopTab,
inputSyntax: inputTab || defaultInputTab,}
).catch((err) => {
console.error(err);
return formatErrorToDisplay(err);
Expand All @@ -54,11 +56,11 @@ const useOutput2 = routeLoader$(async (requestEvent) => {
const outputTab = requestEvent.url.searchParams.get('outputTab') as OutputFramework;
const inputTab = requestEvent.url.searchParams.get('inputTab') as InputSyntax;

const output = await compile(
const output = await compile({
code,
outputTab || defaultBottomTab,
inputTab || defaultInputTab,
).catch((err) => {
output: outputTab || defaultBottomTab,
inputSyntax: inputTab || defaultInputTab,
}).catch((err) => {
console.error(err);
return formatErrorToDisplay(err);
});
Expand All @@ -73,13 +75,19 @@ export default component$(() => {
const inputTab = location.url.searchParams.get('inputTab') as InputSyntax;
const loaderOutput1 = useOutput1().value;
const loaderOutput2 = useOutput2().value;

const showSecondOutput = useSignal(true);
const code = useSignal(initialCode || defaultCode);
const inputSyntax = useSignal<InputSyntax>(inputTab || defaultInputTab);
const output = useSignal(loaderOutput1 || '');
const outputOneFramework = useSignal<OutputFramework>(outputTab || defaultTopTab);
const optionsOne = useStore(getDefaultOptions(outputTab || defaultTopTab), {
deep: true
});
const output2 = useSignal(loaderOutput2 || '');
const outputTwoFramework = useSignal<OutputFramework>(defaultBottomTab);
const optionsTwo = useStore(getDefaultOptions(defaultBottomTab), {
deep: true
})
const visible = useSignal(false);
const isThrottling = useSignal(false);
const isThrottling2 = useSignal(false);
Expand Down Expand Up @@ -112,7 +120,7 @@ export default component$(() => {
});

const throttledCompileOne = $(
async (code: string, outputFramework: OutputFramework, inputSyntax: InputSyntax) => {
async (args: CompileArgs) => {
updateUrl();
if (throttleTimeout1.value) {
clearTimeout(throttleTimeout1.value);
Expand All @@ -121,7 +129,7 @@ export default component$(() => {
throttleTimeout1.value = setTimeout(async () => {
isThrottling.value = true;
try {
output.value = await compile(code, outputFramework, inputSyntax);
output.value = await compile(args);
errorOne.value = '';
} catch (err) {
errorOne.value = formatErrorToDisplay(err);
Expand All @@ -134,7 +142,7 @@ export default component$(() => {
}
isThrottling.value = true;
try {
output.value = await compile(code, outputFramework, inputSyntax);
output.value = await compile(args);
errorOne.value = '';
} catch (err) {
errorOne.value = formatErrorToDisplay(err);
Expand All @@ -145,15 +153,15 @@ export default component$(() => {
);

const throttledCompileTwo = $(
async (code: string, outputFramework: OutputFramework, inputSyntax: InputSyntax) => {
async (args: CompileArgs) => {
if (throttleTimeout2.value) {
clearTimeout(throttleTimeout2.value);
}
if (isThrottling2.value) {
throttleTimeout2.value = setTimeout(async () => {
isThrottling.value = true;
try {
output2.value = await compile(code, outputFramework, inputSyntax);
output2.value = await compile(args);
errorTwo.value = '';
} catch (err) {
errorTwo.value = formatErrorToDisplay(err);
Expand All @@ -165,7 +173,7 @@ export default component$(() => {
}
isThrottling2.value = true;
try {
output2.value = await compile(code, outputFramework, inputSyntax);
output2.value = await compile(args);
errorTwo.value = '';
} catch (err) {
errorTwo.value = formatErrorToDisplay(err);
Expand All @@ -178,8 +186,9 @@ export default component$(() => {
useVisibleTask$(async ({ track }) => {
track(code);
track(outputOneFramework);
track(optionsOne);
try {
await throttledCompileOne(code.value, outputOneFramework.value, inputSyntax.value);
await throttledCompileOne({code: code.value, output: outputOneFramework.value, inputSyntax: inputSyntax.value, outputOptions: optionsOne});
} catch (err) {
console.warn(err);
}
Expand All @@ -188,8 +197,9 @@ export default component$(() => {
useVisibleTask$(async ({ track }) => {
track(code);
track(outputTwoFramework);
track(optionsTwo);
try {
await throttledCompileTwo(code.value, outputTwoFramework.value, inputSyntax.value);
await throttledCompileTwo({code: code.value, output: outputTwoFramework.value, inputSyntax: inputSyntax.value, outputOptions: optionsTwo});
} catch (err) {
console.warn(err);
}
Expand All @@ -198,8 +208,12 @@ export default component$(() => {
// Always reload on window refocus to ensure cloudflare workers are warm
useVisibleTask$(({ cleanup }) => {
const fn = () => {
throttledCompileOne(code.value, outputOneFramework.value, inputSyntax.value);
throttledCompileTwo(code.value, outputTwoFramework.value, inputSyntax.value);
throttledCompileOne({
code: code.value, output: outputOneFramework.value, inputSyntax: inputSyntax.value, outputOptions: optionsOne
});
throttledCompileTwo({
code: code.value, output: outputTwoFramework.value, inputSyntax: inputSyntax.value, outputOptions: optionsTwo
});
};
addEventListener('focus', fn);
cleanup(() => {
Expand All @@ -220,11 +234,12 @@ export default component$(() => {
class="ml-auto max-md:scale-[0.85] -my-2 max-md:-mr-1.5"
value={inputSyntax.value}
onChange$={(syntax: any) => {
compile(
code.value,
syntax === 'jsx' ? 'mitosis' : 'svelte',
inputSyntax.value,
).then((output) => {
compile({
code: code.value,
output: syntax === 'jsx' ? 'mitosis' : 'svelte',
inputSyntax: inputSyntax.value,

}).then((output) => {
code.value = output.replace(/\n?\n?import { useStore } from "..";\n?/g, '');
inputSyntax.value = syntax;
});
Expand Down Expand Up @@ -259,12 +274,16 @@ export default component$(() => {
{visible.value && (
// Workaround weird bug where this doesn't render correctly
// server side
<div class="ml-auto mr-2 max-md:scale-[0.85] mx-md:-my-2 max-md:-mr-1.5 flex gap-2 items-baseline">
<OptionsModal options={optionsOne} target={outputOneFramework} />
<Select
class="ml-auto mr-2 max-md:scale-[0.85] mx-md:-my-2 max-md:-mr-1.5"
value={outputOneFramework.value}
onChange$={(framework: any) => (outputOneFramework.value = framework)}
onChange$={(framework: any) => {
outputOneFramework.value = framework;
Object.assign(optionsOne, getDefaultOptions(framework))
}}
options={outputs}
/>
/></div>
)}
</div>
<div class="h-[50%] max-md:h-auto grow relative">
Expand All @@ -284,20 +303,27 @@ export default component$(() => {
</div>
)}
</div>
<div class="min-h-[50px] max-md:min-h-[40px] max-md:hidden flex items-center border-primary border-opacity-50 border-t -mt-4 pt-4">
<div class={["min-h-[50px] max-md:min-h-[40px] max-md:hidden flex border-primary border-opacity-50 border-t -mt-4 pt-4", showSecondOutput.value ? '' : 'pb-4']}>
{visible.value && (
// Workaround weird bug where this doesn't render correctly
// server side
<div class="ml-auto mr-2 md:mr-6 gap-2 flex items-baseline">
<button class={'px-3 py-1.5 outline-0 rounded text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-purple-990 bg-primary focus:ring-primary bg-opacity-10 border border-primary border-opacity-50 transition-colors duration-200 ease-in-out appearance-none'} onClick$={$(() => {
showSecondOutput.value = !showSecondOutput.value
})}>{showSecondOutput.value ? 'hide' : 'show'}</button>
<OptionsModal options={optionsTwo} target={outputTwoFramework} />
<Select
class="ml-auto mr-2 md:mr-6"
value={outputTwoFramework.value}
onChange$={(framework: any) => (outputTwoFramework.value = framework)}
onChange$={(framework: any) => {
outputTwoFramework.value = framework;
Object.assign(optionsTwo, getDefaultOptions(framework))
}}
options={outputs}
/>
/></div>
)}
</div>

<div class="h-[50%] relative max-md:hidden">
{showSecondOutput.value && <div class="h-[50%] relative max-md:hidden">
<ContentLoaderCode
width={400}
class="ml-16 mt-3 opacity-10 origin-top-left max-md:scale-75 max-md:ml-4"
Expand All @@ -313,7 +339,7 @@ export default component$(() => {
<ErrorWarning errorMessage={errorTwo.value} />
</div>
)}
</div>
</div>}
</div>
</div>
);
Expand Down
Loading
Loading