Customize tags colors (#5032)

* add user defined tag colors

* use DB to store tag colors

* change specific tags_colors to generic tags

* move tag customization to its own page (pt. 1)

* move tag customization to its own page (pt. 2), edit description

* move tag customization to its own page (pt. 3), better default tag mgmt

* move tag customization to its own page (pt. 4), tag creation

* move tag customization to its own page (pt. 5), remove tags settings

* Update VRT

* nitpicking & code rabbit fixing

* remove spaces from tag and better partial Tag typing

* add tag similar to transaction add

* peer review updates (live preview)

* enable keyboard navigation

* fix lint errors

* live input for color picker

* disable 3 digit hex color live input

* add context menu

* add tags link to command bar

* Update VRT

* Update VRT

* fix lint issues

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: youngcw <calebyoung94@gmail.com>
This commit is contained in:
POGMAN
2025-07-07 21:42:26 +02:00
committed by GitHub
parent bd9f0aec89
commit 405c8b986f
102 changed files with 1204 additions and 68 deletions

View File

@@ -48,7 +48,8 @@
"./tokens": "./src/tokens.ts",
"./toggle": "./src/Toggle.tsx",
"./tooltip": "./src/Tooltip.tsx",
"./view": "./src/View.tsx"
"./view": "./src/View.tsx",
"./color-picker": "./src/ColorPicker.tsx"
},
"scripts": {
"generate:icons": "rm src/icons/*/*.tsx; cd src/icons && svgr --template template.ts --index-template index-template.ts --typescript --expand-props start -d . .",

View File

@@ -0,0 +1,135 @@
import { ChangeEvent, ReactNode } from 'react';
import {
ColorPicker as AriaColorPicker,
ColorPickerProps as AriaColorPickerProps,
Dialog,
DialogTrigger,
ColorSwatch as AriaColorSwatch,
ColorSwatchProps,
ColorSwatchPicker as AriaColorSwatchPicker,
ColorSwatchPickerItem,
ColorField,
parseColor,
} from 'react-aria-components';
import { css } from '@emotion/css';
import { Input } from './Input';
import { Popover } from './Popover';
function ColorSwatch(props: ColorSwatchProps) {
return (
<AriaColorSwatch
{...props}
style={({ color }) => ({
background: color.toString('hex'),
width: '32px',
height: '32px',
borderRadius: '4px',
boxShadow: 'inset 0 0 0 1px rgba(0, 0, 0, 0.1)',
})}
/>
);
}
// colors from https://materialui.co/colors
const colorsets = [
['#D32F2F', '#C2185B', '#7B1FA2', '#512DA8', '#303F9F'],
['#1976D2', '#0288D1', '#0097A7', '#00796B', '#388E3C'],
['#689F38', '#AFB42B', '#FBC02D', '#FFA000', '#F57C00'],
['#E64A19', '#5D4037', '#616161', '#455A64', '#690CB0'],
];
function ColorSwatchPicker() {
return (
<>
{colorsets.map((colors, idx) => (
<AriaColorSwatchPicker
key={`colorset-${idx}`}
style={{
display: 'flex',
gap: '8px',
flexWrap: 'wrap',
}}
>
{colors.map(color => (
<ColorSwatchPickerItem
key={color}
color={color}
className={css({
position: 'relative',
outline: 'none',
borderRadius: '4px',
width: 'fit-content',
forcedColorAdjust: 'none',
cursor: 'pointer',
'&[data-selected]::after': {
content: '',
position: 'absolute',
inset: 0,
border: '2px solid black',
outline: '2px solid white',
outlineOffset: '-4px',
borderRadius: 'inherit',
},
})}
>
<ColorSwatch />
</ColorSwatchPickerItem>
))}
</AriaColorSwatchPicker>
))}
</>
);
}
const isColor = (value: string) => /^#[0-9a-fA-F]{6}$/.test(value);
interface ColorPickerProps extends AriaColorPickerProps {
children?: ReactNode;
}
export function ColorPicker({ children, ...props }: ColorPickerProps) {
const onInput = (value: string) => {
if (!isColor(value)) {
return;
}
const color = parseColor(value);
if (color) {
props.onChange?.(color);
}
};
return (
<AriaColorPicker {...props}>
<DialogTrigger>
{children}
<Popover>
<Dialog
style={{
outline: 'none',
padding: '15px',
display: 'flex',
flexDirection: 'column',
gap: '8px',
minWidth: '192px',
maxHeight: 'inherit',
boxSizing: 'border-box',
overflow: 'auto',
}}
>
<ColorSwatchPicker />
<ColorField
onInput={({ target: { value } }: ChangeEvent<HTMLInputElement>) =>
onInput(value)
}
>
<Input placeholder="#RRGGBB" style={{ width: '100px' }} />
</ColorField>
</Dialog>
</Popover>
</DialogTrigger>
</AriaColorPicker>
);
}

View File

@@ -188,6 +188,7 @@ export const theme = {
reportsInnerLabel: 'var(--color-reportsInnerLabel)',
noteTagBackground: 'var(--color-noteTagBackground)',
noteTagBackgroundHover: 'var(--color-noteTagBackgroundHover)',
noteTagDefault: 'var(--color-noteTagDefault)',
noteTagText: 'var(--color-noteTagText)',
budgetOtherMonth: 'var(--color-budgetOtherMonth)',
budgetCurrentMonth: 'var(--color-budgetCurrentMonth)',