Skip to content

Commit f4142d8

Browse files
feat: add new editor frame themes (#591)
* feat: add new editor frame themes * feat: fix isMobile hook * feat: add new editor frame themes * feat: fix isMobile hook * feat: make tabs state agnostic * feat: rework on tab component (lite) * fix: tabName combobox loading * lint: fix * chore: fix error boundary in terminal window tab list * chore: fix error boundary in terminal window tab list * chore: fix typechecking * chore: deps dedupe * chore: update esbuild * feat: align popover sidebar for each property custom * chore: remove todo * chore: fix lint and typecheck * docs(changeset): add new editor frame themes
1 parent ce94a3f commit f4142d8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+6348
-9278
lines changed

.changeset/forty-kiwis-smile.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@codeimage/app': minor
3+
---
4+
5+
add new editor frame themes

.idea/prettier.xml

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/api/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
"sinon": "^15.1.2",
7171
"tsup": "6.7.0",
7272
"tsx": "3.12.7",
73-
"typescript": "^5.1.3",
73+
"typescript": "~5.3.2",
7474
"vite": "^4.3.9",
7575
"vitest": "^0.31.4"
7676
}

apps/codeimage/src/components/PropertyEditor/FrameStyleForm.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {ParentComponent, Show} from 'solid-js';
88
import {appEnvironment} from '../../core/configuration';
99
import {AppLocaleEntries} from '../../i18n';
1010
import {AspectRatioPicker} from './controls/AspectRatioPicker/AspectRatioPicker';
11-
import {CustomColorPicker} from './controls/CustomColorPicker';
11+
import {CustomColorPicker} from './controls/ColorPicker/CustomColorPicker';
1212
import {PanelHeader} from './PanelHeader';
1313
import {PanelRow, TwoColumnPanelRow} from './PanelRow';
1414
import {SuspenseEditorItem} from './SuspenseEditorItem';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {themeVars} from '@codeimage/ui';
2+
import {responsiveStyle, themeTokens} from '@codeui/kit';
3+
import {style} from '@vanilla-extract/css';
4+
5+
export const sidebarPopover = style([
6+
{
7+
width: '360px',
8+
maxWidth: '360px',
9+
display: 'flex',
10+
flexDirection: 'column',
11+
gap: themeTokens.spacing['3'],
12+
},
13+
responsiveStyle({
14+
md: {
15+
maxWidth: 'initial',
16+
},
17+
}),
18+
]);
19+
20+
export const experimentalFlag = style({
21+
color: themeVars.dynamicColors.descriptionTextColor,
22+
fontSize: themeVars.fontSize.xs,
23+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import {Popover, PopoverContent, PopoverTrigger} from '@codeui/kit';
2+
import {useModality} from '@core/hooks/isMobile';
3+
import clsx from 'clsx';
4+
import {FlowProps, JSXElement} from 'solid-js';
5+
import * as styles from './SidebarPopover.css';
6+
7+
interface SidebarPopoverProps {
8+
open: boolean;
9+
onOpenChange: (open: boolean) => void;
10+
input: JSXElement;
11+
contentClass?: string;
12+
modalOnDesktop?: boolean;
13+
}
14+
15+
export function SidebarPopover(props: FlowProps<SidebarPopoverProps>) {
16+
const modality = useModality();
17+
18+
return (
19+
<Popover
20+
placement={modality === 'mobile' ? undefined : 'right-end'}
21+
open={props.open}
22+
modal={props.modalOnDesktop && modality === 'full'}
23+
onOpenChange={open => props.onOpenChange(open)}
24+
>
25+
<PopoverTrigger asChild>{props.input}</PopoverTrigger>
26+
27+
<PopoverContent
28+
variant={'bordered'}
29+
class={clsx(styles.sidebarPopover, props.contentClass)}
30+
>
31+
{props.children}
32+
</PopoverContent>
33+
</Popover>
34+
);
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import {Box, HStack, Text} from '@codeimage/ui';
2+
import {IconButton} from '@codeui/kit';
3+
import {
4+
ExperimentalFeatureTooltip,
5+
ExperimentalIcon,
6+
} from '@ui/ExperimentalFeatureTooltip/ExperimentalFeatureTooltip';
7+
import {FlowProps, Show} from 'solid-js';
8+
import {CloseIcon} from '../../Icons/CloseIcon';
9+
import * as styles from './SidebarPopover.css';
10+
11+
interface SidebarPopoverTitleProps {
12+
onClose: () => void;
13+
14+
experimental?: boolean;
15+
featureName?: string;
16+
}
17+
export function SidebarPopoverTitle(
18+
props: FlowProps<SidebarPopoverTitleProps>,
19+
) {
20+
return (
21+
<Box
22+
display={'flex'}
23+
justifyContent={'spaceBetween'}
24+
alignItems={'center'}
25+
marginBottom={4}
26+
>
27+
<ExperimentalFeatureTooltip feature={props.featureName ?? 'This'}>
28+
<HStack spacing={'2'} alignItems={'flexEnd'}>
29+
<Text weight={'semibold'}>{props.children}</Text>
30+
<Show when={props.experimental}>
31+
<Text class={styles.experimentalFlag} size={'xs'}>
32+
<Box as={'span'} display={'flex'} alignItems={'center'}>
33+
<ExperimentalIcon size={'xs'} />
34+
<Box marginLeft={'1'}>Experimental</Box>
35+
</Box>
36+
</Text>
37+
</Show>
38+
</HStack>
39+
</ExperimentalFeatureTooltip>
40+
41+
<IconButton
42+
size={'xs'}
43+
aria-label={'Close'}
44+
theme={'secondary'}
45+
onClick={() => props.onClose()}
46+
>
47+
<CloseIcon />
48+
</IconButton>
49+
</Box>
50+
);
51+
}

apps/codeimage/src/components/PropertyEditor/WindowStyleForm.tsx

+9-45
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
import {useI18n} from '@codeimage/locale';
22
import {getTerminalState} from '@codeimage/store/editor/terminal';
3-
import {Box, Group, RadioBlock} from '@codeimage/ui';
43
import {createSelectOptions, Select} from '@codeui/kit';
54
import {shadowsLabel} from '@core/configuration/shadow';
6-
import {AVAILABLE_TERMINAL_THEMES} from '@core/configuration/terminal-themes';
75
import {getUmami} from '@core/constants/umami';
86
import {SegmentedField} from '@ui/SegmentedField/SegmentedField';
97
import {SkeletonLine} from '@ui/Skeleton/Skeleton';
10-
import {createMemo, For, ParentComponent, Show} from 'solid-js';
8+
import {createMemo, ParentComponent, Show} from 'solid-js';
119
import {AppLocaleEntries} from '../../i18n';
12-
import {TerminalControlField} from '../TerminalControlField/TerminalControlField';
13-
import {TerminalControlSkeleton} from '../TerminalControlField/TerminalControlFieldSkeleton';
10+
import {TerminalControlField} from './controls/TerminalControlField/TerminalControlField';
1411
import {PanelHeader} from './PanelHeader';
1512
import {FullWidthPanelRow, PanelRow, TwoColumnPanelRow} from './PanelRow';
1613
import {SuspenseEditorItem} from './SuspenseEditorItem';
@@ -72,48 +69,15 @@ export const WindowStyleForm: ParentComponent = () => {
7269
</PanelRow>
7370

7471
<Show when={terminal.state.showHeader}>
75-
<PanelRow for={'frameTerminalTypeField'}>
72+
<PanelRow for={'frameTerminalTypeField'} label={'Window'}>
7673
<FullWidthPanelRow>
77-
<SuspenseEditorItem
78-
fallback={
79-
<Group orientation={'vertical'}>
80-
<For each={Object.values(AVAILABLE_TERMINAL_THEMES.entries)}>
81-
{() => (
82-
<RadioBlock value={0}>
83-
<Box padding={2} width={'100%'}>
84-
<TerminalControlSkeleton />
85-
</Box>
86-
</RadioBlock>
87-
)}
88-
</For>
89-
</Group>
90-
}
91-
>
92-
<TerminalControlField
93-
selectedTerminal={terminal.state.type}
94-
onTerminalChange={terminal.setType}
95-
/>
96-
</SuspenseEditorItem>
97-
</FullWidthPanelRow>
98-
</PanelRow>
99-
</Show>
100-
101-
<Show
102-
when={terminal.state.showHeader && !terminal.state.alternativeTheme}
103-
>
104-
<PanelRow for={'frameTabAccentField'} label={t('frame.tabAccent')}>
105-
<TwoColumnPanelRow>
106-
<SegmentedField
107-
size={'xs'}
108-
adapt
109-
value={terminal.state.accentVisible}
110-
onChange={terminal.setAccentVisible}
111-
items={[
112-
{label: t('common.yes'), value: true},
113-
{label: t('common.no'), value: false},
114-
]}
74+
<TerminalControlField
75+
showAccent={terminal.state.accentVisible}
76+
selectedTerminal={terminal.state.type}
77+
onTerminalChange={terminal.setType}
78+
onShowAccentChange={terminal.setAccentVisible}
11579
/>
116-
</TwoColumnPanelRow>
80+
</FullWidthPanelRow>
11781
</PanelRow>
11882
</Show>
11983

Original file line numberDiff line numberDiff line change
@@ -1,20 +1,10 @@
11
import {DEFAULT_ASPECT_RATIOS} from '@codeimage/config';
2-
import {Box, HStack, Text} from '@codeimage/ui';
3-
import {
4-
As,
5-
IconButton,
6-
Popover,
7-
PopoverContent,
8-
PopoverTrigger,
9-
} from '@codeui/kit';
10-
import {useModality} from '@core/hooks/isMobile';
11-
import {
12-
ExperimentalFeatureTooltip,
13-
ExperimentalIcon,
14-
} from '@ui/ExperimentalFeatureTooltip/ExperimentalFeatureTooltip';
2+
import {Box, Text} from '@codeimage/ui';
3+
import {As} from '@codeui/kit';
154
import {assignInlineVars} from '@vanilla-extract/dynamic';
165
import {createSignal, For, Show} from 'solid-js';
17-
import {CloseIcon} from '../../../Icons/CloseIcon';
6+
import {SidebarPopover} from '../../SidebarPopover/SidebarPopover';
7+
import {SidebarPopoverTitle} from '../../SidebarPopover/SidebarPopoverTitle';
188
import * as styles from './AspetRatioPicker.css';
199

2010
interface AspectRatioPickerProps {
@@ -27,15 +17,11 @@ interface AspectRatioPickerProps {
2717
*/
2818
export function AspectRatioPicker(props: AspectRatioPickerProps) {
2919
const [open, setOpen] = createSignal(false);
30-
const modality = useModality();
3120

3221
return (
33-
<Popover
34-
placement={modality === 'mobile' ? undefined : 'right-start'}
35-
open={open()}
36-
onOpenChange={setOpen}
37-
>
38-
<PopoverTrigger asChild>
22+
<SidebarPopover
23+
contentClass={styles.aspectRatioPopover}
24+
input={
3925
<As component={'div'} class={styles.input}>
4026
<Show when={props.value}>
4127
{aspectRatio => (
@@ -50,77 +36,58 @@ export function AspectRatioPicker(props: AspectRatioPickerProps) {
5036

5137
<Text weight={'semibold'}>{props.value ?? 'Auto'}</Text>
5238
</As>
53-
</PopoverTrigger>
54-
<PopoverContent variant={'bordered'} class={styles.aspectRatioPopover}>
55-
<Box
56-
display={'flex'}
57-
justifyContent={'spaceBetween'}
58-
alignItems={'center'}
59-
marginBottom={4}
60-
>
61-
<ExperimentalFeatureTooltip feature={'Aspect ratio'}>
62-
<HStack spacing={'2'} alignItems={'flexEnd'}>
63-
<Text weight={'semibold'}>Aspect Ratio</Text>
64-
<Text class={styles.aspectRatioCardDetails} size={'xs'}>
65-
<Box as={'span'} display={'flex'} alignItems={'center'}>
66-
<ExperimentalIcon size={'xs'} />
67-
<Box marginLeft={'1'}>Experimental</Box>
68-
</Box>
69-
</Text>
70-
</HStack>
71-
</ExperimentalFeatureTooltip>
72-
73-
<IconButton
74-
size={'xs'}
75-
aria-label={'Close'}
76-
theme={'secondary'}
77-
onClick={() => setOpen(false)}
78-
>
79-
<CloseIcon />
80-
</IconButton>
81-
</Box>
39+
}
40+
open={open()}
41+
onOpenChange={setOpen}
42+
>
43+
<SidebarPopoverTitle
44+
experimental
45+
featureName={'Aspect Ratio'}
46+
onClose={() => setOpen(false)}
47+
>
48+
Aspect ratio
49+
</SidebarPopoverTitle>
8250

83-
<div class={styles.aspectRatioCardList}>
51+
<div class={styles.aspectRatioCardList}>
52+
<div
53+
data-selected={!props.value ? '' : null}
54+
class={styles.aspectRatioCardFull}
55+
onClick={() => props.onChange(null)}
56+
>
8457
<div
85-
data-selected={!props.value ? '' : null}
86-
class={styles.aspectRatioCardFull}
87-
onClick={() => props.onChange(null)}
58+
style={assignInlineVars({
59+
[styles.aspectRatio]: 'auto',
60+
})}
61+
class={styles.aspectRadioCardPreview}
8862
>
89-
<div
90-
style={assignInlineVars({
91-
[styles.aspectRatio]: 'auto',
92-
})}
93-
class={styles.aspectRadioCardPreview}
94-
>
95-
<Box marginY={3}>Auto</Box>
96-
</div>
63+
<Box marginY={3}>Auto</Box>
9764
</div>
65+
</div>
9866

99-
<For each={DEFAULT_ASPECT_RATIOS}>
100-
{ratio => (
101-
<div
102-
data-selected={props.value === ratio.ratio ? '' : null}
103-
class={styles.aspectRatioCard}
104-
onClick={() => props.onChange(ratio.ratio)}
105-
>
106-
<div class={styles.aspectRatioCardPreviewWrapper}>
107-
<div
108-
style={assignInlineVars({
109-
[styles.aspectRatio]: ratio.ratio,
110-
})}
111-
class={styles.aspectRadioCardPreview}
112-
>
113-
{ratio.name}
114-
</div>
67+
<For each={DEFAULT_ASPECT_RATIOS}>
68+
{ratio => (
69+
<div
70+
data-selected={props.value === ratio.ratio ? '' : null}
71+
class={styles.aspectRatioCard}
72+
onClick={() => props.onChange(ratio.ratio)}
73+
>
74+
<div class={styles.aspectRatioCardPreviewWrapper}>
75+
<div
76+
style={assignInlineVars({
77+
[styles.aspectRatio]: ratio.ratio,
78+
})}
79+
class={styles.aspectRadioCardPreview}
80+
>
81+
{ratio.name}
11582
</div>
116-
<span class={styles.aspectRatioCardDetails}>
117-
{ratio.resolution.join('x')}
118-
</span>
11983
</div>
120-
)}
121-
</For>
122-
</div>
123-
</PopoverContent>
124-
</Popover>
84+
<span class={styles.aspectRatioCardDetails}>
85+
{ratio.resolution.join('x')}
86+
</span>
87+
</div>
88+
)}
89+
</For>
90+
</div>
91+
</SidebarPopover>
12592
);
12693
}

apps/codeimage/src/components/PropertyEditor/controls/ColorPicker.tsx apps/codeimage/src/components/PropertyEditor/controls/ColorPicker/ColorPicker.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
} from 'solid-js';
2020
import {ColorPickerPresetItem} from './ColorPickerPresetItem';
2121
import * as styles from './CustomColorPicker.css';
22-
import {ImagePicker} from './ImagePicker/ImagePicker';
22+
import {ImagePicker} from '../ImagePicker/ImagePicker';
2323

2424
const enum ColorPickerSelectionMode {
2525
gradient = 'gradient',

0 commit comments

Comments
 (0)