Refactored AppDesigner
parent
96abb53576
commit
b3f30c3ab7
|
@ -1,7 +1,6 @@
|
|||
<template>
|
||||
<Drawer
|
||||
v-model:visible="$appState.designer.active"
|
||||
header="Theme Designer"
|
||||
position="right"
|
||||
class="designer !w-screen md:!w-[48rem]"
|
||||
:modal="false"
|
||||
|
@ -14,142 +13,19 @@
|
|||
footer: '!p-5'
|
||||
}"
|
||||
>
|
||||
<Tabs v-model:value="$appState.designer.activeTab" :lazy="deferredTabs">
|
||||
<TabList>
|
||||
<Tab value="0">Base</Tab>
|
||||
<Tab value="1">Primitive</Tab>
|
||||
<Tab value="2">Semantic</Tab>
|
||||
<Tab value="3">Component</Tab>
|
||||
<Tab value="4">Custom</Tab>
|
||||
</TabList>
|
||||
<TabPanels class="!px-0">
|
||||
<TabPanel value="0">
|
||||
<div class="text-lg font-semibold mb-2">Choose a Theme to Get Started</div>
|
||||
<span class="block text-muted-color leading-6 mb-4">Begin by selecting a built-in theme as a foundation, continue editing your current theme, or import a Figma tokens file.</span>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-col gap-4 border border-surface-200 dark:border-surface-700 rounded-md p-4">
|
||||
<span class="font-semibold">Base Theme</span>
|
||||
<span class="text-muted-color">Variety of built-in themes with distinct characteristics.</span>
|
||||
<SelectButton v-model="$appState.preset" @update:modelValue="onPresetChange" :options="presetOptions" optionLabel="label" optionValue="value" :allowEmpty="false" />
|
||||
<template #header>
|
||||
<div class="flex items-center gap-2">
|
||||
<Button v-if="$appState.designer.activeView !== 'dashboard'" variant="text" severity="secondary" rounded type="button" icon="pi pi-chevron-left" @click="openDashboard" />
|
||||
<span class="font-bold text-xl">{{ viewTitle }}</span>
|
||||
</div>
|
||||
<Divider>OR</Divider>
|
||||
<div class="flex flex-col gap-4 border border-surface-200 dark:border-surface-700 rounded-md p-4 items-start">
|
||||
<span class="font-semibold">Load Theme</span>
|
||||
<span class="text-muted-color">Continue editing the theme files stored locally.</span>
|
||||
<Button label="Restore from local storage" class="!px-3 !py-2" severity="secondary" @click="loadFromLocalStorage" />
|
||||
</div>
|
||||
<Divider>OR</Divider>
|
||||
<div class="flex flex-col gap-4 border border-surface-200 dark:border-surface-700 rounded-md p-4">
|
||||
<div class="flex items-center gap-4">
|
||||
<span class="font-semibold">Import Figma Tokens </span>
|
||||
<Tag value="PRO" severity="contrast"></Tag>
|
||||
</div>
|
||||
<span class="text-muted-color leading-6">Export the token studio json file and import to the Visual Editor. This feature is currently under development.</span>
|
||||
<FileUpload mode="basic" disabled pt:root:class="!justify-start" />
|
||||
</div>
|
||||
</div>
|
||||
</TabPanel>
|
||||
<TabPanel value="1">
|
||||
<div>
|
||||
<form @keydown="onKeyDown" class="flex flex-col gap-3">
|
||||
<DesignBorderRadius />
|
||||
<DesignColors />
|
||||
</form>
|
||||
</div>
|
||||
</TabPanel>
|
||||
<TabPanel value="2">
|
||||
<Accordion :value="['0', '1']" multiple>
|
||||
<AccordionPanel value="0">
|
||||
<AccordionHeader>Common</AccordionHeader>
|
||||
<AccordionContent>
|
||||
<div>
|
||||
<form @keydown="onKeyDown" class="flex flex-col gap-3">
|
||||
<DesignGeneral />
|
||||
<DesignFormField />
|
||||
<DesignList />
|
||||
<DesignNavigation />
|
||||
<DesignOverlay />
|
||||
</form>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionPanel>
|
||||
</template>
|
||||
|
||||
<AccordionPanel value="1">
|
||||
<AccordionHeader>Color Scheme</AccordionHeader>
|
||||
<AccordionContent>
|
||||
<Tabs value="cs-0">
|
||||
<TabList>
|
||||
<Tab value="cs-0">Light</Tab>
|
||||
<Tab value="cs-1">Dark</Tab>
|
||||
</TabList>
|
||||
<TabPanels class="!px-0">
|
||||
<TabPanel value="cs-0">
|
||||
<form @keydown="onKeyDown">
|
||||
<DesignCS :value="$appState.designer.preset.semantic.colorScheme.light" />
|
||||
</form>
|
||||
</TabPanel>
|
||||
<TabPanel value="cs-1">
|
||||
<form @keydown="onKeyDown">
|
||||
<DesignCS :value="$appState.designer.preset.semantic.colorScheme.dark" />
|
||||
</form>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</AccordionContent>
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
</TabPanel>
|
||||
<TabPanel value="3">
|
||||
<form @keydown="onKeyDown">
|
||||
<DesignComponent />
|
||||
</form>
|
||||
</TabPanel>
|
||||
<TabPanel value="4">
|
||||
<span class="leading-6 text-muted-color">Extend the theming system with your own design tokens e.g. <span class="font-medium">accent.color</span>. Do not use curly braces in the name field.</span>
|
||||
<ul class="flex flex-col gap-4 list-none p-0 mx-0 my-4">
|
||||
<li v-for="(token, index) of $appState.designer.customTokens" :key="index" class="first:border-t border-b border-surface-200 dark:border-surface-300 py-2">
|
||||
<div class="flex items-center gap-4">
|
||||
<label class="flex items-center gap-2 flex-auto">
|
||||
<span class="text-sm">Name</span>
|
||||
<input v-model="token['name']" type="text" class="border border-surface-300 dark:border-surface-600 rounded-lg py-2 px-2 w-full" />
|
||||
</label>
|
||||
<label class="flex items-center gap-2 flex-auto">
|
||||
<span class="text-sm">Value</span>
|
||||
<input v-model="token['value']" type="text" class="border border-surface-300 dark:border-surface-600 rounded-lg py-2 px-2 w-full" />
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
@click="removeToken(index)"
|
||||
class="cursor-pointer inline-flex items-center justify-center ms-auto w-8 h-8 rounded-full bg-red-50 hover:bg-red-100 text-red-600 dark:bg-red-400/10 dark:hover:bg-red-400/20 dark:text-red-400 transition-colors duration-200 focus:outline focus:outline-offset-2 focus:outline-red-600 focus:dark:outline-red-400"
|
||||
>
|
||||
<i class="pi pi-times" />
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="flex justify-between">
|
||||
<button
|
||||
type="button"
|
||||
@click="addToken"
|
||||
class="px-3 py-2 bg-zinc-950 hover:bg-zinc-800 text-white dark:bg-white dark:hover:bg-gray-100 dark:text-black rounded-md font-medium cursor-pointer transition-colors duration-200 focus:outline focus:outline-offset-2 focus:outline-zinc-950 focus:dark:outline-white"
|
||||
>
|
||||
Add New
|
||||
</button>
|
||||
<button
|
||||
v-if="$appState.designer.customTokens.length"
|
||||
type="button"
|
||||
@click="saveTokens"
|
||||
class="px-3 py-2 bg-zinc-950 hover:bg-zinc-800 text-white dark:bg-white dark:hover:bg-gray-100 dark:text-black rounded-md font-medium cursor-pointer transition-colors duration-200 focus:outline focus:outline-offset-2 focus:outline-zinc-950 focus:dark:outline-white"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
<DesignDashboard v-if="$appState.designer.activeView === 'dashboard'" />
|
||||
<DesignCreateTheme v-else-if="$appState.designer.activeView === 'create_theme'" />
|
||||
<DesignEditor v-else-if="$appState.designer.activeView === 'editor'" :deferred="deferredTabs" />
|
||||
|
||||
<template #footer>
|
||||
<div class="flex justify-between gap-2">
|
||||
<div v-if="$appState.designer.active.view === 'editor'" class="flex justify-between gap-2">
|
||||
<button
|
||||
type="button"
|
||||
@click="download"
|
||||
|
@ -159,9 +35,9 @@
|
|||
Download
|
||||
</button>
|
||||
<button
|
||||
v-if="$appState.designer.activeTab !== '0'"
|
||||
type="button"
|
||||
@click="apply"
|
||||
icon="pi pi-download"
|
||||
class="px-3 py-2 bg-zinc-950 hover:bg-zinc-800 text-white dark:bg-white dark:hover:bg-gray-100 dark:text-black rounded-md font-medium cursor-pointer transition-colors duration-200 focus:outline focus:outline-offset-2 focus:outline-zinc-950 focus:dark:outline-white"
|
||||
>
|
||||
Apply
|
||||
|
@ -172,172 +48,40 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import EventBus from '@/app/AppEventBus';
|
||||
import { $dt, updatePreset, usePreset } from '@primevue/themes';
|
||||
import Aura from '@primevue/themes/aura';
|
||||
import Lara from '@primevue/themes/lara';
|
||||
import Material from '@primevue/themes/material';
|
||||
import Nora from '@primevue/themes/nora';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const presets = {
|
||||
Aura,
|
||||
Material,
|
||||
Lara,
|
||||
Nora
|
||||
};
|
||||
import { $dt } from '@primevue/themes';
|
||||
|
||||
export default {
|
||||
provide() {
|
||||
return {
|
||||
$preset: computed(() => this.$appState.designer.preset)
|
||||
designerUtils: {
|
||||
refreshACTokens: this.refreshACTokens,
|
||||
saveTheme: this.saveTheme
|
||||
}
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
deferredTabs: true,
|
||||
presetOptions: [
|
||||
{ label: 'Aura', value: 'Aura' },
|
||||
{ label: 'Material', value: 'Material' },
|
||||
{ label: 'Lara', value: 'Lara' },
|
||||
{ label: 'Nora', value: 'Nora' }
|
||||
]
|
||||
deferredTabs: true
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (!this.$appState.designer.preset) {
|
||||
this.$appState.designer.preset = presets.Aura;
|
||||
this.replaceColorPalette();
|
||||
this.generateACTokens(null, this.$appState.designer.preset);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
apply() {
|
||||
this.saveTheme();
|
||||
updatePreset(this.$appState.designer.preset);
|
||||
EventBus.emit('theme-palette-change');
|
||||
onShow() {
|
||||
this.deferredTabs = false;
|
||||
},
|
||||
onHide() {
|
||||
this.deferredTabs = true;
|
||||
},
|
||||
download() {
|
||||
const basePreset = this.$appState.preset;
|
||||
const theme = JSON.stringify(this.$appState.designer.preset, null, 4).replace(/"([^"]+)":/g, '$1:');
|
||||
const textContent = `import { createApp } from "vue";
|
||||
import PrimeVue from "primevue/config";
|
||||
import ${basePreset} from "@primevue/themes/${basePreset.toLowerCase()}";
|
||||
import { definePreset } from "@primevue/themes";
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
const MyPreset = definePreset(${basePreset}, ${theme});
|
||||
|
||||
app.use(PrimeVue, {
|
||||
theme: {
|
||||
preset: MyPreset
|
||||
// TODO: Fetch from endpoint
|
||||
},
|
||||
});
|
||||
app.mount("#app");
|
||||
`;
|
||||
const blob = new Blob([textContent], { type: 'text/plain' });
|
||||
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const a = document.createElement('a');
|
||||
|
||||
a.href = url;
|
||||
a.download = 'mytheme.js';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
},
|
||||
onPresetChange(value) {
|
||||
this.$appState.preset = value;
|
||||
const newPreset = presets[value];
|
||||
|
||||
if (value === 'Material') {
|
||||
document.body.classList.add('material');
|
||||
this.$primevue.config.ripple = true;
|
||||
} else {
|
||||
document.body.classList.remove('material');
|
||||
}
|
||||
|
||||
this.$appState.designer.preset = newPreset;
|
||||
|
||||
this.replaceColorPalette();
|
||||
|
||||
usePreset(this.$appState.designer.preset);
|
||||
apply() {
|
||||
saveTheme();
|
||||
updatePreset(this.$appState.designer.theme.preset);
|
||||
EventBus.emit('theme-palette-change');
|
||||
},
|
||||
saveTheme() {
|
||||
const localState = {
|
||||
themes: {
|
||||
defaultTheme: {
|
||||
preset: this.$appState.designer.preset,
|
||||
customTokens: this.$appState.designer.customTokens
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
localStorage.setItem(this.$appState.designer.key, JSON.stringify(localState));
|
||||
},
|
||||
loadFromLocalStorage() {
|
||||
const localState = localStorage.getItem(this.$appState.designer.key);
|
||||
|
||||
if (localState) {
|
||||
const parsedLocalState = JSON.parse(localState);
|
||||
|
||||
if (parsedLocalState?.themes?.defaultTheme) {
|
||||
const defaultTheme = parsedLocalState.themes.defaultTheme;
|
||||
|
||||
if (defaultTheme.preset) {
|
||||
this.$appState.designer.preset = defaultTheme.preset;
|
||||
usePreset(this.$appState.designer.preset);
|
||||
}
|
||||
|
||||
if (defaultTheme.customTokens) {
|
||||
this.$appState.designer.customTokens = defaultTheme.customTokens;
|
||||
this.refreshACTokens();
|
||||
}
|
||||
|
||||
this.$toast.add({ severity: 'success', summary: 'Success', detail: 'Theme loaded to Designer.', life: 3000 });
|
||||
}
|
||||
} else {
|
||||
this.$toast.add({ severity: 'warn', summary: 'Error', detail: 'No saved theme found.', life: 3000 });
|
||||
}
|
||||
},
|
||||
addToken() {
|
||||
this.$appState.designer.customTokens = [...this.$appState.designer.customTokens, ...[{}]];
|
||||
},
|
||||
removeToken(index) {
|
||||
this.$appState.designer.customTokens.splice(index, 1);
|
||||
},
|
||||
saveTokens() {
|
||||
this.$appState.designer.preset.extend = {};
|
||||
this.$appState.designer.customTokens.forEach((token) => {
|
||||
this.$appState.designer.preset.extend[this.transformTokenName(token.name)] = token.value;
|
||||
});
|
||||
|
||||
this.refreshACTokens();
|
||||
this.saveTheme();
|
||||
this.$toast.add({ severity: 'success', summary: 'Success', detail: 'Tokens saved', life: 3000 });
|
||||
},
|
||||
replaceColorPalette() {
|
||||
this.$appState.designer.preset.semantic.primary = this.$appState.designer.preset.primitive.emerald;
|
||||
this.$appState.designer.preset.semantic.colorScheme.light.surface = { ...{ 0: '#ffffff' }, ...this.$appState.designer.preset.primitive.slate };
|
||||
this.$appState.designer.preset.semantic.colorScheme.dark.surface = { ...{ 0: '#ffffff' }, ...this.$appState.designer.preset.primitive.zinc };
|
||||
},
|
||||
transformTokenName(name) {
|
||||
if (name && name.trim().length) {
|
||||
let tokenNameSections = name.split('.');
|
||||
let transformedName = '';
|
||||
|
||||
tokenNameSections.forEach((section, index) => {
|
||||
transformedName += index === 0 ? section : section.charAt(0).toUpperCase() + section.substring(1);
|
||||
});
|
||||
|
||||
return transformedName;
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
// TODO: Save to DB or Local Storage
|
||||
console.log('theme saved');
|
||||
},
|
||||
camelCaseToDotCase(name) {
|
||||
return name.replace(/([a-z])([A-Z])/g, '$1.$2').toLowerCase();
|
||||
|
@ -359,26 +103,33 @@ app.mount("#app");
|
|||
const tokenValue = $dt(tokenName).value;
|
||||
const isColor = tokenName.includes('color') || tokenName.includes('background') || regex.test(tokenName);
|
||||
|
||||
this.$appState.designer.acTokens.push({ token: tokenName, label: '{' + tokenName + '}', variable: $dt(tokenName).variable, value: tokenValue, isColor: isColor });
|
||||
this.$appState.designer.theme.acTokens.push({ token: tokenName, label: '{' + tokenName + '}', variable: $dt(tokenName).variable, value: tokenValue, isColor: isColor });
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
refreshACTokens() {
|
||||
this.$appState.designer.acTokens = [];
|
||||
this.generateACTokens(null, this.$appState.designer.preset);
|
||||
this.$appState.designer.theme.acTokens = [];
|
||||
this.generateACTokens(null, this.$appState.designer.theme.preset);
|
||||
console.log('refresh tokens');
|
||||
},
|
||||
onKeyDown(event) {
|
||||
if (event.code === 'Enter' || event.code === 'NumpadEnter') {
|
||||
this.apply();
|
||||
event.preventDefault();
|
||||
openDashboard() {
|
||||
this.$appState.designer.activeView = 'dashboard';
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
this.deferredTabs = false;
|
||||
},
|
||||
onHide() {
|
||||
this.deferredTabs = true;
|
||||
computed: {
|
||||
viewTitle() {
|
||||
const view = this.$appState.designer.activeView;
|
||||
|
||||
if (view === 'dashboard') {
|
||||
return 'Theme Designer';
|
||||
} else if (view === 'create_theme') {
|
||||
return 'Create Theme';
|
||||
} else if (view === 'editor') {
|
||||
return this.$appState.designer.theme.name;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
<template>
|
||||
<section class="mb-6">
|
||||
<span class="block text-lg font-semibold mb-2">Theme Name</span>
|
||||
<input v-model="themeName" type="text" autocomplete="off" class="px-3 py-2 rounded-md border border-surface-300 dark:border-surface-700 flex-1" />
|
||||
</section>
|
||||
|
||||
<section class="mb-6">
|
||||
<div class="text-lg font-semibold mb-2">Foundation</div>
|
||||
<span class="block text-muted-color leading-6 mb-4">Start by choosing a built-in theme as a foundation, or import your own design by uploading a tokens.json file created with the Prime UI Kit and Tokens Studio.</span>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-col gap-4 border border-surface-200 dark:border-surface-700 rounded-md p-4">
|
||||
<span class="font-semibold">Base Theme</span>
|
||||
<span class="text-muted-color">Variety of built-in themes with distinct characteristics.</span>
|
||||
<div class="flex justify-between">
|
||||
<SelectButton v-model="basePreset" :options="presetOptions" optionLabel="label" optionValue="value" :allowEmpty="false" />
|
||||
<button
|
||||
type="button"
|
||||
@click="createThemeFromPreset"
|
||||
icon="pi pi-download"
|
||||
class="px-3 py-2 bg-zinc-950 hover:bg-zinc-800 text-white dark:bg-white dark:hover:bg-gray-100 dark:text-black rounded-md font-medium cursor-pointer transition-colors duration-200 focus:outline focus:outline-offset-2 focus:outline-zinc-950 focus:dark:outline-white"
|
||||
>
|
||||
Create
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider>OR</Divider>
|
||||
|
||||
<div class="flex flex-col gap-4 border border-surface-200 dark:border-surface-700 rounded-md p-4">
|
||||
<span class="font-semibold">Import Figma Tokens </span>
|
||||
<span class="text-muted-color leading-6">Export the token.json file from Figma Token Studio and import to the Visual Editor.</span>
|
||||
<div class="flex justify-between">
|
||||
<FileUpload mode="basic" pt:root:class="!justify-start" @select="onFileSelect($event)" />
|
||||
<button
|
||||
type="button"
|
||||
@click="createThemeFromFigma"
|
||||
icon="pi pi-download"
|
||||
class="px-3 py-2 bg-zinc-950 hover:bg-zinc-800 text-white dark:bg-white dark:hover:bg-gray-100 dark:text-black rounded-md font-medium cursor-pointer transition-colors duration-200 focus:outline focus:outline-offset-2 focus:outline-zinc-950 focus:dark:outline-white"
|
||||
>
|
||||
Create
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { usePreset } from '@primevue/themes';
|
||||
import Aura from '@primevue/themes/aura';
|
||||
import Lara from '@primevue/themes/lara';
|
||||
import Material from '@primevue/themes/material';
|
||||
import Nora from '@primevue/themes/nora';
|
||||
|
||||
const presets = {
|
||||
Aura,
|
||||
Material,
|
||||
Lara,
|
||||
Nora
|
||||
};
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
|
||||
return {
|
||||
designerApiBase: runtimeConfig.public.designerApiBase
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
themeName: null,
|
||||
basePreset: 'Aura',
|
||||
figmaData: null,
|
||||
presetOptions: [
|
||||
{ label: 'Aura', value: 'Aura' },
|
||||
{ label: 'Material', value: 'Material' },
|
||||
{ label: 'Lara', value: 'Lara' },
|
||||
{ label: 'Nora', value: 'Nora' }
|
||||
]
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
replaceColorPalette() {
|
||||
this.$appState.designer.theme.preset.semantic.primary = this.$appState.designer.theme.preset.primitive.emerald;
|
||||
this.$appState.designer.theme.preset.semantic.colorScheme.light.surface = { ...{ 0: '#ffffff' }, ...this.$appState.designer.theme.preset.primitive.slate };
|
||||
this.$appState.designer.theme.preset.semantic.colorScheme.dark.surface = { ...{ 0: '#ffffff' }, ...this.$appState.designer.theme.preset.primitive.zinc };
|
||||
},
|
||||
async createThemeFromPreset() {
|
||||
const newPreset = presets[this.basePreset];
|
||||
|
||||
if (this.basePreset === 'Material') {
|
||||
document.body.classList.add('material');
|
||||
this.$primevue.config.ripple = true;
|
||||
} else {
|
||||
document.body.classList.remove('material');
|
||||
}
|
||||
|
||||
const { error } = await $fetch(this.designerApiBase + '/theme/create', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
name: this.themeName,
|
||||
preset: newPreset,
|
||||
license_key: this.$appState.designer.licenseKey
|
||||
}
|
||||
});
|
||||
|
||||
if (error) {
|
||||
this.$toast.add({ severity: 'error', summary: 'An error occured', detail: error.message, life: 3000 });
|
||||
} else {
|
||||
this.$appState.designer.theme = {
|
||||
name: this.themeName,
|
||||
preset: newPreset,
|
||||
customTokens: [],
|
||||
acTokens: []
|
||||
};
|
||||
this.replaceColorPalette();
|
||||
usePreset(newPreset);
|
||||
|
||||
this.$appState.designer.activeView = 'editor';
|
||||
}
|
||||
},
|
||||
onFileSelect(event) {
|
||||
const file = event.files[0];
|
||||
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = (e) => {
|
||||
this.figmaData = e.target.result;
|
||||
};
|
||||
|
||||
reader.onerror = (e) => {
|
||||
this.$toast.add({ severity: 'error', summary: 'An error occured', detail: 'Unable to read file', life: 3000 });
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
},
|
||||
async createThemeFromFigma() {
|
||||
const { data: newPreset, error } = await $fetch(this.designerApiBase + '/theme/figma', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
name: this.themeName,
|
||||
figma_tokens: this.figmaData,
|
||||
license_key: this.$appState.designer.licenseKey
|
||||
}
|
||||
});
|
||||
|
||||
if (error) {
|
||||
this.$toast.add({ severity: 'error', summary: 'An error occured', detail: error.message, life: 3000 });
|
||||
} else {
|
||||
this.$appState.designer.theme = {
|
||||
name: this.themeName,
|
||||
preset: newPreset,
|
||||
customTokens: [],
|
||||
acTokens: []
|
||||
};
|
||||
this.replaceColorPalette();
|
||||
usePreset(newPreset);
|
||||
|
||||
this.$appState.designer.activeView = 'editor';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,236 @@
|
|||
<template>
|
||||
<div class="text-lg font-semibold mb-2">License Key</div>
|
||||
<span class="block text-muted-color leading-6 mb-4"
|
||||
>A license can be purchased from PrimeStore, if you do not have a license key, you are still able to use the Designer in free tier. <NuxtLink to="/designer" class="doc-link">Learn more</NuxtLink> about the Theme Designer.</span
|
||||
>
|
||||
<form @submit.prevent class="flex gap-4">
|
||||
<input v-model="$appState.designer.licenseKey" type="password" autocomplete="off" class="px-3 py-2 rounded-md border border-surface-300 dark:border-surface-700 flex-1" />
|
||||
<button
|
||||
type="button"
|
||||
@click="activate(false)"
|
||||
icon="pi pi-download"
|
||||
:disabled="!$appState.designer.licenseKey"
|
||||
class="px-3 py-2 bg-zinc-950 hover:bg-zinc-800 text-white dark:bg-white dark:hover:bg-gray-100 dark:text-black rounded-md font-medium cursor-pointer transition-colors duration-200 focus:outline focus:outline-offset-2 focus:outline-zinc-950 focus:dark:outline-white disabled:opacity-60"
|
||||
>
|
||||
Activate
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="text-lg font-semibold mb-2 mt-6">My Themes</div>
|
||||
<span class="block text-muted-color leading-6 mb-4">Continue editing your existing themes or build a new one.</span>
|
||||
<div class="flex flex-wrap gap-6">
|
||||
<button type="button" class="rounded-xl h-36 w-36 bg-transparent border border-gray-200 dark:border-gray-700 hover:border-gray-400 dark:hover:border-gray-500 text-black dark:text-white" @click="openNewTheme">
|
||||
<i class="pi pi-plus !text-2xl"></i>
|
||||
</button>
|
||||
<template v-if="loading">
|
||||
<Skeleton class="!rounded-xl !h-36 !w-36">-</Skeleton>
|
||||
<Skeleton class="!rounded-xl !h-36 !w-36">-</Skeleton>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div v-for="theme of $appState.designer.themes" :key="theme.t_key" class="flex flex-col gap-2 relative">
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-xl h-36 w-36 px-4 overflow-hidden text-ellipsis bg-transparent border border-gray-200 dark:border-gray-700 hover:border-gray-400 dark:hover:border-gray-500 text-black dark:text-white"
|
||||
@click="loadTheme(theme)"
|
||||
>
|
||||
<span class="text-2xl uppercase">{{ abbrThemeName(theme) }}</span>
|
||||
</button>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<div class="group flex items-center gap-2 relative">
|
||||
<input v-model="theme.t_name" type="text" class="w-24 text-sm px-2 py-1" maxlength="100" @blur="renameTheme(theme)" />
|
||||
<i class="hidden group-hover:block pi pi-pencil !text-sm absolute top-50 right-0 text-muted-color"></i>
|
||||
</div>
|
||||
<span class="text-muted-color text-xs">{{ formatTimestamp(theme.last_updated) }}</span>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleMenuOptions($event, theme)"
|
||||
class="bg-surface-100 dark:bg-surface-800 hover:bg-surface-200 dark:hover:bg-surface-700 text-black dark:text-white flex absolute top-2 right-2 w-8 h-8 rounded-full items-center justify-center"
|
||||
>
|
||||
<i class="pi pi-ellipsis-v" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<Menu ref="themeMenu" :model="themeOptions" :popup="true" />
|
||||
|
||||
<ConfirmDialog group="designer"></ConfirmDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { usePreset } from '@primevue/themes';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
|
||||
return {
|
||||
designerApiBase: runtimeConfig.public.designerApiBase
|
||||
};
|
||||
},
|
||||
inject: ['designerUtils'],
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
currentTheme: null,
|
||||
confirmDialogVisible: false,
|
||||
themeOptions: [
|
||||
{
|
||||
label: 'Delete',
|
||||
icon: 'pi pi-times',
|
||||
command: () => {
|
||||
this.$confirm.require({
|
||||
group: 'designer',
|
||||
message: 'Are you sure you want to delete this theme?',
|
||||
header: 'Confirmation',
|
||||
icon: 'pi pi-exclamation-triangle',
|
||||
rejectProps: {
|
||||
severity: 'secondary'
|
||||
},
|
||||
accept: () => {
|
||||
this.deleteTheme(this.currentTheme);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Duplicate',
|
||||
icon: 'pi pi-copy',
|
||||
command: () => {
|
||||
this.duplicateTheme(this.currentTheme);
|
||||
}
|
||||
},
|
||||
{ label: 'Download', icon: 'pi pi-download' }
|
||||
]
|
||||
};
|
||||
},
|
||||
created() {
|
||||
if (!this.$appState.designer.licenseKey) {
|
||||
const keyValue = localStorage.getItem(this.$appState.designer.localStoreKey);
|
||||
|
||||
if (keyValue) {
|
||||
this.$appState.designer.licenseKey = keyValue;
|
||||
this.activate(true);
|
||||
}
|
||||
} else {
|
||||
this.loadThemes();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async activate(silent) {
|
||||
this.loading = true;
|
||||
const { data, error } = await $fetch(this.designerApiBase + '/license/' + this.$appState.designer.licenseKey);
|
||||
|
||||
if (error) {
|
||||
this.$toast.add({ severity: 'error', summary: 'An Error Occurred', detail: error.message, life: 3000 });
|
||||
} else {
|
||||
if (data) {
|
||||
if (!silent) {
|
||||
this.$toast.add({ severity: 'success', summary: 'Success', detail: 'License is activated.', life: 3000 });
|
||||
}
|
||||
|
||||
this.loadThemes();
|
||||
localStorage.setItem(this.$appState.designer.localStoreKey, this.$appState.designer.licenseKey);
|
||||
} else {
|
||||
this.$toast.add({ severity: 'warn', summary: 'Unable to Activate', detail: 'Invalid key', life: 3000 });
|
||||
this.$appState.designer.themes = [];
|
||||
}
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
},
|
||||
openNewTheme() {
|
||||
this.$appState.designer.activeView = 'create_theme';
|
||||
},
|
||||
async loadThemes() {
|
||||
const { data, error } = await $fetch(this.designerApiBase + '/theme/list/' + this.$appState.designer.licenseKey);
|
||||
|
||||
if (error) {
|
||||
this.$toast.add({ severity: 'error', summary: 'An Error Occurred', detail: error.message, life: 3000 });
|
||||
} else {
|
||||
this.$appState.designer.themes = data;
|
||||
}
|
||||
},
|
||||
async loadTheme(theme) {
|
||||
const { data, error } = await $fetch(this.designerApiBase + '/theme/' + theme.t_key);
|
||||
|
||||
if (error) {
|
||||
this.$toast.add({ severity: 'error', summary: 'An Error Occurred', detail: 'Code: ' + error.code, life: 3000 });
|
||||
} else {
|
||||
this.$appState.designer.theme = {
|
||||
name: data.t_name,
|
||||
preset: JSON.parse(data.preset),
|
||||
customTokens: [],
|
||||
acTokens: []
|
||||
};
|
||||
|
||||
usePreset(this.$appState.designer.theme.preset);
|
||||
this.designerUtils.refreshACTokens();
|
||||
this.$appState.designer.activeView = 'editor';
|
||||
}
|
||||
},
|
||||
async renameTheme(theme) {
|
||||
const { error } = await $fetch(this.designerApiBase + '/theme/rename/' + theme.t_key, {
|
||||
method: 'PATCH',
|
||||
body: {
|
||||
name: theme.t_name,
|
||||
license_key: this.$appState.designer.licenseKey
|
||||
}
|
||||
});
|
||||
|
||||
if (error) {
|
||||
this.$toast.add({ severity: 'error', summary: 'An Error Occurred', detail: error.message, life: 3000 });
|
||||
}
|
||||
},
|
||||
async deleteTheme(theme) {
|
||||
const { error } = await $fetch(this.designerApiBase + '/theme/delete/' + theme.t_key, {
|
||||
method: 'DELETE',
|
||||
body: {
|
||||
license_key: this.$appState.designer.licenseKey
|
||||
}
|
||||
});
|
||||
|
||||
if (error) {
|
||||
this.$toast.add({ severity: 'error', summary: 'An Error Occurred', detail: error.message, life: 3000 });
|
||||
} else {
|
||||
this.loadThemes();
|
||||
}
|
||||
},
|
||||
async duplicateTheme(theme) {
|
||||
const { error } = await $fetch(this.designerApiBase + '/theme/duplicate/' + theme.t_key, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
license_key: this.$appState.designer.licenseKey
|
||||
}
|
||||
});
|
||||
|
||||
if (error) {
|
||||
this.$toast.add({ severity: 'error', summary: 'An Error Occurred', detail: error.message, life: 3000 });
|
||||
} else {
|
||||
this.loadThemes();
|
||||
}
|
||||
},
|
||||
formatTimestamp(timestamp) {
|
||||
const date = new Date(timestamp);
|
||||
|
||||
const options = {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
};
|
||||
|
||||
return date.toLocaleString('en-US', options);
|
||||
},
|
||||
toggleMenuOptions(event, theme) {
|
||||
this.currentTheme = theme;
|
||||
this.$refs.themeMenu.toggle(event);
|
||||
},
|
||||
abbrThemeName(theme) {
|
||||
return theme.t_name ? theme.t_name.substring(0, 2) : 'U';
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,96 @@
|
|||
<template>
|
||||
<Tabs v-model:value="$appState.designer.activeTab" :lazy="deferred">
|
||||
<TabList>
|
||||
<Tab value="0">Primitive</Tab>
|
||||
<Tab value="1">Semantic</Tab>
|
||||
<Tab value="2" :disabled="!isComponentRoute">Component</Tab>
|
||||
<Tab value="3">Custom</Tab>
|
||||
</TabList>
|
||||
<TabPanels class="!px-0">
|
||||
<TabPanel value="0">
|
||||
<div>
|
||||
<form @keydown="onKeyDown" class="flex flex-col gap-3">
|
||||
<DesignBorderRadius />
|
||||
<DesignColors />
|
||||
</form>
|
||||
</div>
|
||||
</TabPanel>
|
||||
<TabPanel value="1">
|
||||
<Accordion :value="['0', '1']" multiple>
|
||||
<AccordionPanel value="0">
|
||||
<AccordionHeader>Common</AccordionHeader>
|
||||
<AccordionContent>
|
||||
<div>
|
||||
<form @keydown="onKeyDown" class="flex flex-col gap-3">
|
||||
<DesignGeneral />
|
||||
<DesignFormField />
|
||||
<DesignList />
|
||||
<DesignNavigation />
|
||||
<DesignOverlay />
|
||||
</form>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionPanel>
|
||||
|
||||
<AccordionPanel value="1">
|
||||
<AccordionHeader>Color Scheme</AccordionHeader>
|
||||
<AccordionContent>
|
||||
<Tabs value="cs-0">
|
||||
<TabList>
|
||||
<Tab value="cs-0">Light</Tab>
|
||||
<Tab value="cs-1">Dark</Tab>
|
||||
</TabList>
|
||||
<TabPanels class="!px-0">
|
||||
<TabPanel value="cs-0">
|
||||
<form @keydown="onKeyDown">
|
||||
<DesignCS :value="$appState.designer.theme.preset.semantic.colorScheme.light" />
|
||||
</form>
|
||||
</TabPanel>
|
||||
<TabPanel value="cs-1">
|
||||
<form @keydown="onKeyDown">
|
||||
<DesignCS :value="$appState.designer.theme.preset.semantic.colorScheme.dark" />
|
||||
</form>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</AccordionContent>
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
</TabPanel>
|
||||
<TabPanel value="2">
|
||||
<form @keydown="onKeyDown">
|
||||
<DesignComponent />
|
||||
</form>
|
||||
</TabPanel>
|
||||
<TabPanel value="3">
|
||||
<DesignCustomTokens />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
deferred: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onKeyDown(event) {
|
||||
if (event.code === 'Enter' || event.code === 'NumpadEnter') {
|
||||
this.apply();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isComponentRoute() {
|
||||
const components = this.$appState.designer.theme?.preset?.components;
|
||||
|
||||
return components ? components[this.$route.name] != null : false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
<label :for="inputId" class="text-sm text-zinc-700 dark:text-white capitalize">{{ label }}</label>
|
||||
<label :for="inputId" class="text-sm text-zinc-700 dark:text-white block capitalize text-ellipsis overflow-hidden w-full whitespace-nowrap" :title="label">{{ label }}</label>
|
||||
<div :id="id" class="relative">
|
||||
<AutoComplete
|
||||
:modelValue="modelValue"
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<section class="flex flex-col gap-3">
|
||||
<section v-if="isComponentRoute" class="flex flex-col gap-3">
|
||||
<div class="text-lg font-semibold capitalize">{{ componentKey }}</div>
|
||||
<template v-for="(value, name) in tokens" :key="name">
|
||||
<DesignComponentSection v-if="name !== 'colorScheme'" :componentKey="componentKey" :path="name" />
|
||||
|
@ -27,19 +27,23 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['$preset'],
|
||||
computed: {
|
||||
componentKey() {
|
||||
return this.$route.name;
|
||||
},
|
||||
isComponentRoute() {
|
||||
const components = this.$appState.designer.theme?.preset?.components;
|
||||
|
||||
return components ? components[this.componentKey] != null : false;
|
||||
},
|
||||
tokens() {
|
||||
return this.$preset.components[this.componentKey];
|
||||
return this.$appState.designer.theme?.preset?.components[this.componentKey];
|
||||
},
|
||||
lightTokens() {
|
||||
return this.$preset.components[this.componentKey].colorScheme.light;
|
||||
return this.$appState.designer.theme?.preset?.components[this.componentKey].colorScheme.light;
|
||||
},
|
||||
darkTokens() {
|
||||
return this.$preset.components[this.componentKey].colorScheme.dark;
|
||||
return this.$appState.designer.theme?.preset?.components[this.componentKey].colorScheme.dark;
|
||||
},
|
||||
hasColorScheme() {
|
||||
return this.tokens.colorScheme != undefined;
|
|
@ -24,7 +24,6 @@ export default {
|
|||
default: null
|
||||
}
|
||||
},
|
||||
inject: ['$preset'],
|
||||
methods: {
|
||||
camelCaseToSpaces(val) {
|
||||
return val.replace(/([a-z])([A-Z])/g, '$1 $2');
|
||||
|
@ -67,7 +66,7 @@ export default {
|
|||
.join(' ');
|
||||
},
|
||||
tokens() {
|
||||
return this.getObjectProperty(this.$preset.components[this.componentKey], this.path);
|
||||
return this.getObjectProperty(this.$appState.designer.theme.preset.components[this.componentKey], this.path);
|
||||
},
|
||||
nestedTokens() {
|
||||
const groups = {};
|
|
@ -0,0 +1,80 @@
|
|||
<template>
|
||||
<span class="leading-6 text-muted-color">Extend the theming system with your own design tokens e.g. <span class="font-medium">accent.color</span>. Do not use curly braces in the name field.</span>
|
||||
<ul class="flex flex-col gap-4 list-none p-0 mx-0 my-4">
|
||||
<li v-for="(token, index) of $appState.designer.customTokens" :key="index" class="first:border-t border-b border-surface-200 dark:border-surface-300 py-2">
|
||||
<div class="flex items-center gap-4">
|
||||
<label class="flex items-center gap-2 flex-auto">
|
||||
<span class="text-sm">Name</span>
|
||||
<input v-model="token['name']" type="text" class="border border-surface-300 dark:border-surface-600 rounded-lg py-2 px-2 w-full" />
|
||||
</label>
|
||||
<label class="flex items-center gap-2 flex-auto">
|
||||
<span class="text-sm">Value</span>
|
||||
<input v-model="token['value']" type="text" class="border border-surface-300 dark:border-surface-600 rounded-lg py-2 px-2 w-full" />
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
@click="removeToken(index)"
|
||||
class="cursor-pointer inline-flex items-center justify-center ms-auto w-8 h-8 rounded-full bg-red-50 hover:bg-red-100 text-red-600 dark:bg-red-400/10 dark:hover:bg-red-400/20 dark:text-red-400 transition-colors duration-200 focus:outline focus:outline-offset-2 focus:outline-red-600 focus:dark:outline-red-400"
|
||||
>
|
||||
<i class="pi pi-times" />
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="flex justify-between">
|
||||
<button
|
||||
type="button"
|
||||
@click="addToken"
|
||||
class="px-3 py-2 bg-zinc-950 hover:bg-zinc-800 text-white dark:bg-white dark:hover:bg-gray-100 dark:text-black rounded-md font-medium cursor-pointer transition-colors duration-200 focus:outline focus:outline-offset-2 focus:outline-zinc-950 focus:dark:outline-white"
|
||||
>
|
||||
Add New
|
||||
</button>
|
||||
<button
|
||||
v-if="$appState.designer.theme.customTokens.length"
|
||||
type="button"
|
||||
@click="saveTokens"
|
||||
class="px-3 py-2 bg-zinc-950 hover:bg-zinc-800 text-white dark:bg-white dark:hover:bg-gray-100 dark:text-black rounded-md font-medium cursor-pointer transition-colors duration-200 focus:outline focus:outline-offset-2 focus:outline-zinc-950 focus:dark:outline-white"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['designer'],
|
||||
methods: {
|
||||
addToken() {
|
||||
this.$appState.designer.theme.customTokens = [...this.$appState.designer.theme.customTokens, ...[{}]];
|
||||
},
|
||||
removeToken(index) {
|
||||
this.$appState.designer.theme.customTokens.splice(index, 1);
|
||||
},
|
||||
saveTokens() {
|
||||
this.$appState.designer.theme.preset.extend = {};
|
||||
this.$appState.designer.theme.customTokens.forEach((token) => {
|
||||
this.$appState.designer.theme.preset.extend[this.transformTokenName(token.name)] = token.value;
|
||||
});
|
||||
|
||||
designer.refreshACTokens();
|
||||
designer.saveTheme();
|
||||
|
||||
this.$toast.add({ severity: 'success', summary: 'Success', detail: 'Tokens saved', life: 3000 });
|
||||
},
|
||||
transformTokenName(name) {
|
||||
if (name && name.trim().length) {
|
||||
let tokenNameSections = name.split('.');
|
||||
let transformedName = '';
|
||||
|
||||
tokenNameSections.forEach((section, index) => {
|
||||
transformedName += index === 0 ? section : section.charAt(0).toUpperCase() + section.substring(1);
|
||||
});
|
||||
|
||||
return transformedName;
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,28 @@
|
|||
<template>
|
||||
<Fieldset legend="Rounded" :toggleable="true">
|
||||
<section class="grid grid-cols-4 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.primitive.borderRadius.none" label="None" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.primitive.borderRadius.xs" label="Extra Small" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.primitive.borderRadius.sm" label="Small" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.primitive.borderRadius.md" label="Medium" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.primitive.borderRadius.lg" label="Large" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.primitive.borderRadius.xl" label="Extra Large" />
|
||||
</div>
|
||||
</section>
|
||||
</Fieldset>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {};
|
||||
</script>
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<Fieldset legend="Colors" :toggleable="true">
|
||||
<template v-for="(value, key) of $preset.primitive" :key="key">
|
||||
<template v-for="(value, key) of $appState.designer.theme.preset.primitive" :key="key">
|
||||
<section v-if="key !== 'borderRadius'" class="flex justify-between items-center mb-4">
|
||||
<div class="flex gap-2 items-center">
|
||||
<span class="text-sm capitalize block w-20">{{ key }}</span>
|
||||
<input :value="$preset.primitive[key]['500']" @input="onColorChange($event, key)" type="color" />
|
||||
<input :value="$appState.designer.theme.preset.primitive[key]['500']" @input="onColorChange($event, key)" type="color" />
|
||||
</div>
|
||||
<DesignColorPalette :value="$preset.primitive[key]" />
|
||||
<DesignColorPalette :value="$appState.designer.theme.preset.primitive[key]" />
|
||||
</section>
|
||||
</template>
|
||||
</Fieldset>
|
||||
|
@ -16,10 +16,9 @@
|
|||
import { palette } from '@primevue/themes';
|
||||
|
||||
export default {
|
||||
inject: ['$preset'],
|
||||
methods: {
|
||||
onColorChange(event, color) {
|
||||
this.$preset.primitive[color] = palette(event.target.value);
|
||||
this.$appState.designer.theme.preset.primitive[color] = palette(event.target.value);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,67 @@
|
|||
<template>
|
||||
<Fieldset legend="Form Field" :toggleable="true">
|
||||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Base</div>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.formField.paddingX" label="Padding X" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.formField.paddingY" label="Padding Y" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.formField.borderRadius" label="Border Radius" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.formField.transitionDuration" label="Transition Duration" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Small</div>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.formField.sm.paddingX" label="Padding X" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.formField.sm.paddingY" label="Padding Y" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.formField.sm.fontSize" label="Font Size" />
|
||||
</div>
|
||||
<div></div>
|
||||
</section>
|
||||
|
||||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Large</div>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.formField.lg.paddingX" label="Padding X" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.formField.lg.paddingY" label="Padding Y" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.formField.lg.fontSize" label="Font Size" />
|
||||
</div>
|
||||
<div></div>
|
||||
</section>
|
||||
|
||||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Focus Ring</div>
|
||||
<section class="grid grid-cols-4 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.formField.focusRing.width" label="Width" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.formField.focusRing.style" label="Style" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.formField.focusRing.color" label="Color" type="color" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.formField.focusRing.offset" label="Offset" />
|
||||
</div>
|
||||
</section>
|
||||
</Fieldset>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {};
|
||||
</script>
|
|
@ -0,0 +1,61 @@
|
|||
<template>
|
||||
<Fieldset legend="General" :toggleable="true">
|
||||
<section class="flex justify-between items-center mb-4">
|
||||
<div class="flex gap-2 items-center">
|
||||
<span class="text-sm">Primary</span>
|
||||
<input :value="$appState.designer.theme.preset.semantic.primary['500']" @input="onPrimaryColorChange($event)" type="color" />
|
||||
</div>
|
||||
<DesignColorPalette :value="$appState.designer.theme.preset.semantic.primary" />
|
||||
</section>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.transitionDuration" label="Transition Duration" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.disabledOpacity" label="Disabled Opacity" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.iconSize" label="Icon Size" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.anchorGutter" label="Anchor Gutter" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.content.borderRadius" label="Border Radius" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.mask.transitionDuration" label="Mask Transition Dur." />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
</section>
|
||||
|
||||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Focus Ring</div>
|
||||
<section class="grid grid-cols-4 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.focusRing.width" label="Width" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.focusRing.style" label="Style" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.focusRing.color" label="Color" type="color" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.focusRing.offset" label="Offset" />
|
||||
</div>
|
||||
</section>
|
||||
</Fieldset>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { palette } from '@primevue/themes';
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
onPrimaryColorChange(event) {
|
||||
this.$appState.designer.theme.preset.semantic.primary = palette(event.target.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -3,13 +3,13 @@
|
|||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Container</div>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.list.padding" label="Padding" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.list.padding" label="Padding" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.list.gap" label="Gap" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.list.gap" label="Gap" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.list.header.padding" label="Header Padding" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.list.header.padding" label="Header Padding" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
</section>
|
||||
|
@ -17,27 +17,25 @@
|
|||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Option</div>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.list.option.padding" label="Padding" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.list.option.padding" label="Padding" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.list.option.borderRadius" label="Border Radius" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.list.option.borderRadius" label="Border Radius" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Option Group</div>
|
||||
<section class="grid grid-cols-4 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.list.optionGroup.padding" label="Padding" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.list.optionGroup.padding" label="Padding" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.list.optionGroup.fontWeight" label="Font Weight" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.list.optionGroup.fontWeight" label="Font Weight" />
|
||||
</div>
|
||||
</section>
|
||||
</Fieldset>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['$preset']
|
||||
};
|
||||
export default {};
|
||||
</script>
|
|
@ -3,10 +3,10 @@
|
|||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">List</div>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.navigation.list.padding" label="Padding" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.navigation.list.padding" label="Padding" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.navigation.list.gap" label="Gap" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.navigation.list.gap" label="Gap" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
|
@ -15,13 +15,13 @@
|
|||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Item</div>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.navigation.item.padding" label="Padding" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.navigation.item.padding" label="Padding" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.navigation.item.borderRadius" label="Border Radius" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.navigation.item.borderRadius" label="Border Radius" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.navigation.item.gap" label="Gap" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.navigation.item.gap" label="Gap" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
</section>
|
||||
|
@ -29,10 +29,10 @@
|
|||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Submenu Label</div>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.navigation.submenuLabel.padding" label="Padding" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.navigation.submenuLabel.padding" label="Padding" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.navigation.submenuLabel.fontWeight" label="Font Weight" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.navigation.submenuLabel.fontWeight" label="Font Weight" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
|
@ -41,7 +41,7 @@
|
|||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Submenu Icon</div>
|
||||
<section class="grid grid-cols-4 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.navigation.submenuIcon.size" label="Size" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.navigation.submenuIcon.size" label="Size" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
|
@ -51,7 +51,5 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['$preset']
|
||||
};
|
||||
export default {};
|
||||
</script>
|
|
@ -3,10 +3,10 @@
|
|||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Select</div>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.overlay.select.borderRadius" label="Border Radius" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.overlay.select.borderRadius" label="Border Radius" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.overlay.select.shadow" label="Shadow" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.overlay.select.shadow" label="Shadow" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
|
@ -15,13 +15,13 @@
|
|||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Popover</div>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.overlay.popover.borderRadius" label="Border Radius" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.overlay.popover.borderRadius" label="Border Radius" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.overlay.popover.padding" label="Padding" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.overlay.popover.padding" label="Padding" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.overlay.popover.shadow" label="Shadow" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.overlay.popover.shadow" label="Shadow" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
</section>
|
||||
|
@ -29,13 +29,13 @@
|
|||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Modal</div>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.overlay.modal.borderRadius" label="Border Radius" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.overlay.modal.borderRadius" label="Border Radius" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.overlay.modal.padding" label="Padding" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.overlay.modal.padding" label="Padding" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.overlay.modal.shadow" label="Shadow" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.overlay.modal.shadow" label="Shadow" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
</section>
|
||||
|
@ -43,7 +43,7 @@
|
|||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Navigation</div>
|
||||
<section class="grid grid-cols-4 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.overlay.navigation.shadow" label="Shadow" />
|
||||
<DesignTokenField v-model="$appState.designer.theme.preset.semantic.overlay.navigation.shadow" label="Shadow" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
|
@ -53,7 +53,5 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['$preset']
|
||||
};
|
||||
export default {};
|
||||
</script>
|
|
@ -1,30 +0,0 @@
|
|||
<template>
|
||||
<Fieldset legend="Rounded" :toggleable="true">
|
||||
<section class="grid grid-cols-4 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.primitive.borderRadius.none" label="None" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.primitive.borderRadius.xs" label="Extra Small" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.primitive.borderRadius.sm" label="Small" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.primitive.borderRadius.md" label="Medium" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.primitive.borderRadius.lg" label="Large" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.primitive.borderRadius.xl" label="Extra Large" />
|
||||
</div>
|
||||
</section>
|
||||
</Fieldset>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['$preset']
|
||||
};
|
||||
</script>
|
|
@ -1,69 +0,0 @@
|
|||
<template>
|
||||
<Fieldset legend="Form Field" :toggleable="true">
|
||||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Base</div>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.formField.paddingX" label="Padding X" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.formField.paddingY" label="Padding Y" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.formField.borderRadius" label="Border Radius" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.formField.transitionDuration" label="Transition Duration" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Small</div>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.formField.sm.paddingX" label="Padding X" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.formField.sm.paddingY" label="Padding Y" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.formField.sm.fontSize" label="Font Size" />
|
||||
</div>
|
||||
<div></div>
|
||||
</section>
|
||||
|
||||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Large</div>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.formField.lg.paddingX" label="Padding X" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.formField.lg.paddingY" label="Padding Y" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.formField.lg.fontSize" label="Font Size" />
|
||||
</div>
|
||||
<div></div>
|
||||
</section>
|
||||
|
||||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Focus Ring</div>
|
||||
<section class="grid grid-cols-4 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.formField.focusRing.width" label="Width" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.formField.focusRing.style" label="Style" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.formField.focusRing.color" label="Color" type="color" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.formField.focusRing.offset" label="Offset" />
|
||||
</div>
|
||||
</section>
|
||||
</Fieldset>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['$preset']
|
||||
};
|
||||
</script>
|
|
@ -1,62 +0,0 @@
|
|||
<template>
|
||||
<Fieldset legend="General" :toggleable="true">
|
||||
<section class="flex justify-between items-center mb-4">
|
||||
<div class="flex gap-2 items-center">
|
||||
<span class="text-sm">Primary</span>
|
||||
<input :value="$preset.semantic.primary['500']" @input="onPrimaryColorChange($event)" type="color" />
|
||||
</div>
|
||||
<DesignColorPalette :value="$preset.semantic.primary" />
|
||||
</section>
|
||||
<section class="grid grid-cols-4 mb-3 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.transitionDuration" label="Transition Duration" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.disabledOpacity" label="Disabled Opacity" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.iconSize" label="Icon Size" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.anchorGutter" label="Anchor Gutter" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.content.borderRadius" label="Border Radius" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.mask.transitionDuration" label="Mask Transition Dur." />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
<div class="flex flex-col gap-1"></div>
|
||||
</section>
|
||||
|
||||
<div class="text-sm mb-1 font-semibold text-surface-950 dark:text-surface-0">Focus Ring</div>
|
||||
<section class="grid grid-cols-4 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.focusRing.width" label="Width" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.focusRing.style" label="Style" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.focusRing.color" label="Color" type="color" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<DesignTokenField v-model="$preset.semantic.focusRing.offset" label="Offset" />
|
||||
</div>
|
||||
</section>
|
||||
</Fieldset>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { palette } from '@primevue/themes';
|
||||
|
||||
export default {
|
||||
inject: ['$preset'],
|
||||
methods: {
|
||||
onPrimaryColorChange(event) {
|
||||
this.$preset.semantic.primary = palette(event.target.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -97,7 +97,8 @@ export default defineNuxtConfig({
|
|||
runtimeConfig: {
|
||||
public: {
|
||||
contextPath: baseUrl,
|
||||
DEV_ENV: PROCESS_ENV.DEV_ENV
|
||||
DEV_ENV: PROCESS_ENV.DEV_ENV,
|
||||
designerApiBase: ''
|
||||
}
|
||||
},
|
||||
css: ['primeicons/primeicons.css', '@/assets/styles/flags.css', '@docsearch/css/dist/style.css', '@/assets/styles/tailwind/main.css', '@/assets/styles/layout/layout.scss']
|
||||
|
|
|
@ -15,12 +15,18 @@ const $appState = {
|
|||
announcement: null,
|
||||
storageKey: 'primevue',
|
||||
designer: {
|
||||
key: 'primevue-designer-theme',
|
||||
localStoreKey: 'primevue-designer-licensekey',
|
||||
licenseKey: null,
|
||||
active: false,
|
||||
activeView: 'dashboard',
|
||||
activeTab: '0',
|
||||
theme: {
|
||||
name: null,
|
||||
preset: null,
|
||||
customTokens: [],
|
||||
acTokens: []
|
||||
},
|
||||
themes: []
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue