Skip to content
Merged
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
39 changes: 20 additions & 19 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,18 @@ import Editor from '@components/Editor.vue'
import Layout from '@components/Layout.vue'
import Select from '@components/Select.vue'
import { Switch } from '@headlessui/vue'

const mathOptions = ['always', 'parens-division', 'parens'].map(value => ({ value }))
import { defaultStore, supportedOptions, renderOptions, type OptionStore } from './options'

let input = $ref(LESS_DATA)
let output = $ref<string | undefined>('')
let errorMessage = $ref('')
let hash = $ref('')
let store: Less.Options & { activeVersion: string } = reactive({
let store: OptionStore & { activeVersion: string } = reactive({
activeVersion: '4.x',
math: 'parens-division',
strictUnits: false
...defaultStore
})
let loadingLessJS = $ref(false)
const options = $computed(() => supportedOptions(store.activeVersion))

const serialize = () => {
const newHash = '#' + utoa(JSON.stringify({
Expand All @@ -29,8 +28,7 @@ const serialize = () => {
}

const updateVue = () => {
const { math, strictUnits } = store
window.less.render(input, { math, strictUnits }, (error, result) => {
window.less.render(input, renderOptions(store, store.activeVersion), (error, result) => {
if (error) {
errorMessage = error.message
output = ''
Expand Down Expand Up @@ -80,18 +78,21 @@ onMounted(() => {
</template>
<template #options>
<h3>Options</h3>
<div class="option">
<span>Math mode</span>
<Select v-model="store.math" :options="mathOptions" />
</div>
<Switch
v-model="store.strictUnits"
class="option switch"
:class="{ on: store.strictUnits }"
>
<span>Strict units</span>
<span class="switch-track" aria-hidden="true"><span class="switch-thumb"></span></span>
</Switch>
<template v-for="o in options" :key="o.key">
<div v-if="o.type === 'select'" class="option">
<span>{{ o.label }}</span>
<Select v-model="store[o.key]" :options="o.values.map(value => ({ value }))" />
</div>
<Switch
v-else
v-model="store[o.key]"
class="option switch"
:class="{ on: store[o.key] }"
>
<span>{{ o.label }}</span>
<span class="switch-track" aria-hidden="true"><span class="switch-thumb"></span></span>
</Switch>
</template>
</template>
<template #preview>
<editor v-model:value="output" readOnly />
Expand Down
62 changes: 62 additions & 0 deletions src/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Compile options the playground exposes, gated by the Less version that
// introduced (`min`, inclusive) or dropped (`max`, exclusive) them.
// Only options meaningful for a single-string browser render live here.

export interface OptionStore {
strictMath: boolean
math: string
strictUnits: boolean
unitMode: string
collapseNesting: boolean
compress: boolean
}

type SwitchKey = { [K in keyof OptionStore]: OptionStore[K] extends boolean ? K : never }[keyof OptionStore]
type SelectKey = Exclude<keyof OptionStore, SwitchKey>

export type OptionDescriptor =
| { key: SwitchKey; label: string; type: 'switch'; min?: string; max?: string }
| { key: SelectKey; label: string; type: 'select'; values: string[]; min?: string; max?: string }

export const defaultStore: OptionStore = {
strictMath: false,
math: 'parens-division',
strictUnits: false,
unitMode: 'preserve',
collapseNesting: false,
compress: false
}

// Evidence: `math` replaced `strictMath` in less.js 76c10345 (first tag v3.7.0);
// `unitMode` (strictUnits deprecated alias), `collapseNesting` and the rejection
// of `compress` land in 5.0.0-alpha (packages/less/lib/options.js on `alpha`).
const all: OptionDescriptor[] = [
{ key: 'strictMath', label: 'Strict math', type: 'switch', max: '3.7.0' },
{ key: 'math', label: 'Math mode', type: 'select', values: ['always', 'parens-division', 'parens'], min: '3.7.0' },
{ key: 'strictUnits', label: 'Strict units', type: 'switch', max: '5.0.0' },
{ key: 'unitMode', label: 'Unit mode', type: 'select', values: ['loose', 'strict', 'preserve'], min: '5.0.0' },
{ key: 'collapseNesting', label: 'Collapse nesting', type: 'switch', min: '5.0.0' },
{ key: 'compress', label: 'Minify output (deprecated)', type: 'switch', max: '5.0.0' }
]

// Numeric core only: "5.0.0-alpha.2" compares as 5.0.0; "4.x" as 4.0.0.
const parse = (v: string) =>
v.split('-')[0].split('.').map(p => Number(p) || 0)

const compare = (a: string, b: string) => {
const [x, y] = [parse(a), parse(b)]
for (let i = 0; i < 3; i++) {
if ((x[i] ?? 0) !== (y[i] ?? 0)) return (x[i] ?? 0) - (y[i] ?? 0)
}
return 0
}

export const supportedOptions = (version: string) =>
all.filter(o =>
(!o.min || compare(version, o.min) >= 0) &&
(!o.max || compare(version, o.max) < 0)
)

// The only object that reaches less.render: valid keys for `version`, nothing else.
export const renderOptions = (store: OptionStore, version: string) =>
Object.fromEntries(supportedOptions(version).map(o => [o.key, store[o.key]]))
Loading