22import type { PropertyField } from ' @craftile/types' ;
33import { Dialog } from ' @ark-ui/vue/dialog' ;
44import useI18n from ' ../composables/i18n' ;
5+ import { toTitleCase } from ' ../utils/strings' ;
56import TypographyPresetEditor from ' ./TypographyPresetEditor.vue' ;
67import TypographyPresetPreview from ' ./TypographyPresetPreview.vue' ;
78
@@ -13,6 +14,7 @@ interface Font {
1314}
1415
1516interface TypographyPresetData {
17+ name? : string ;
1618 fontFamily: Font | null ;
1719 fontStyle: string ;
1820 fontWeight: string ;
@@ -33,6 +35,8 @@ const { t } = useI18n();
3335
3436const editingPreset = ref <string | null >(null );
3537const editModalOpen = ref (false );
38+ const isEditingName = ref (false );
39+ const editingNameValue = ref (' ' );
3640
3741const themePresetIds = computed (() => {
3842 return Object .keys (props .field .presets || {});
@@ -51,6 +55,18 @@ const editingPresetValue = computed({
5155 }
5256});
5357
58+ const editingPresetDisplayName = computed (() => {
59+ if (! editingPreset .value || ! editingPresetValue .value ) {
60+ return ' ' ;
61+ }
62+ return editingPresetValue .value .name || toTitleCase (editingPreset .value );
63+ });
64+
65+ const canRenameCurrentPreset = computed (() => {
66+ if (! editingPreset .value ) return false ;
67+ return ! themePresetIds .value .includes (editingPreset .value );
68+ });
69+
5470function onEditPreset(id : string ) {
5571 editingPreset .value = id ;
5672 editModalOpen .value = true ;
@@ -85,6 +101,47 @@ function onDeletePreset(id: string) {
85101 model .value = rest ;
86102}
87103
104+ function startEditingName() {
105+ if (! canRenameCurrentPreset .value ) {
106+ return ;
107+ }
108+
109+ isEditingName .value = true ;
110+ editingNameValue .value = editingPresetValue .value ?.name || editingPresetDisplayName .value ;
111+
112+ nextTick (() => {
113+ const input = document .querySelector (' .preset-name-input' ) as HTMLInputElement ;
114+ input ?.focus ();
115+ input ?.select ();
116+ });
117+ }
118+
119+ function saveEditingName() {
120+ if (! editingPresetValue .value ) {
121+ return ;
122+ }
123+
124+ editingPresetValue .value = {
125+ ... editingPresetValue .value ,
126+ name: editingNameValue .value ?.trim (),
127+ };
128+
129+ isEditingName .value = false ;
130+ }
131+
132+ function cancelEditingName() {
133+ isEditingName .value = false ;
134+ editingNameValue .value = ' ' ;
135+ }
136+
137+ function handleNameKeydown(e : KeyboardEvent ) {
138+ if (e .key === ' Enter' ) {
139+ saveEditingName ();
140+ } else if (e .key === ' Escape' ) {
141+ cancelEditingName ();
142+ }
143+ }
144+
88145function handleEditorDelete() {
89146 if (! editingPreset .value ) {
90147 return ;
@@ -148,7 +205,35 @@ const canDeleteCurrentPreset = computed(() => {
148205 <Dialog .Positioner class =" flex fixed z-50 top-14 left-14 bottom-0 w-75 items-center justify-center" >
149206 <Dialog .Content class =" bg-white shadow flex flex-col w-full h-full overflow-hidden" >
150207 <header class =" flex-none h-12 border-b border-neutral-200 flex gap-3 px-4 items-center justify-between" >
151- <Dialog .Title class =" capitalize" >{{ t('Editing') }} {{ editingPreset?.replace('-', ' ') }}</Dialog .Title >
208+ <div class =" flex items-center gap-2 flex-1 min-w-0" >
209+ <span class =" text-sm text-gray-600 flex-none" >{{ t('Editing') }}</span >
210+
211+ <input
212+ v-if =" isEditingName"
213+ v-model =" editingNameValue"
214+ type =" text"
215+ maxlength =" 50"
216+ class =" preset-name-input flex-1 min-w-0 px-2 py-1 text-sm font-medium border border-blue-500 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"
217+ @blur =" saveEditingName"
218+ @keydown =" handleNameKeydown"
219+ />
220+
221+ <button
222+ v-else
223+ type =" button"
224+ class =" flex-1 min-w-0 text-left px-2 py-1 text-sm font-medium rounded truncate"
225+ :class =" canRenameCurrentPreset ? 'hover:bg-gray-100 cursor-pointer' : 'cursor-default'"
226+ :title =" canRenameCurrentPreset ? t('Click to rename') : t('Theme preset (cannot be renamed)')"
227+ @click =" startEditingName"
228+ >
229+ {{ editingPresetDisplayName }}
230+ <i-heroicons-pencil
231+ v-if =" canRenameCurrentPreset"
232+ class =" inline-block w-3 h-3 ml-1 text-gray-400"
233+ />
234+ </button >
235+ </div >
236+
152237 <Dialog .CloseTrigger class =" cursor-pointer rounded-lg p-0.5 text-neutral-700 hover:bg-neutral-300" >
153238 <i-heroicons-x-mark class =" w-5 h-5" />
154239 </Dialog .CloseTrigger >
0 commit comments