Spreadsheet-UX op een echte relationele engine
Deze repository wordt als momentopname gepubliceerd: een clone bevat de volledige inhoud, maar geen commitgeschiedenis.
import type { ReactNode } from 'react';
import { currencySymbol, type ColumnType } from '@wellformed/shared';
export function KeyIcon(): ReactNode {
return (
<svg
width="10"
height="10"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.4"
strokeLinecap="round"
aria-hidden="true"
>
<circle cx="8" cy="8" r="4.5" />
<path d="M11.5 11.5 20 20M16 16l3-3" />
</svg>
);
}
export function SearchIcon(): ReactNode {
return (
<svg
width="13"
height="13"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.2"
strokeLinecap="round"
aria-hidden="true"
>
<circle cx="10.5" cy="10.5" r="7" />
<path d="M16 16l5 5" />
</svg>
);
}
/** Compact type marker shown in headers and the schema outline. */
export const TYPE_LABEL: Record<ColumnType, string> = {
text: 'abc',
number: '123',
currency: '¤',
date: 'date',
checkbox: '☑',
select: 'enum',
link: 'fk',
formula: 'fx',
rollup: 'Σ',
};
/**
* The same marker, but a money column wears the sign the workspace actually
* uses. A header that says `$` over a column of euros is a small lie told on
* every screen.
*/
export function typeMark(type: ColumnType, currency: string): string {
return type === 'currency' ? currencySymbol(currency) : TYPE_LABEL[type];
}
/** Palette for select values. Stable per label so a status keeps its colour. */
const NAMED_COLORS: Record<string, [string, string]> = {
Draft: ['#EEF2F6', '#5A6B80'],
Invoiced: ['#E7EDFC', '#2050D8'],
Paid: ['#E2F3EC', '#0B7A55'],
Overdue: ['#FBEAE9', '#B3261E'],
West: ['#E7EDFC', '#2050D8'],
East: ['#E2F3EC', '#0B7A55'],
North: ['#FBF1DC', '#A16207'],
South: ['#F3E8FA', '#7C3AED'],
Sales: ['#E7EDFC', '#2050D8'],
Ops: ['#FBF1DC', '#A16207'],
Finance: ['#E2F3EC', '#0B7A55'],
};
const FALLBACK_COLORS: [string, string][] = [
['#E7EDFC', '#2050D8'],
['#E2F3EC', '#0B7A55'],
['#FBF1DC', '#A16207'],
['#F3E8FA', '#7C3AED'],
['#FBEAE9', '#B3261E'],
['#E4F1F6', '#0E6E86'],
];
export function selectColors(value: string): [string, string] {
const named = NAMED_COLORS[value];
if (named) return named;
// Hash the label so a user-defined option gets a consistent colour without
// anyone having to pick one.
let hash = 0;
for (let i = 0; i < value.length; i++) hash = (hash * 31 + value.charCodeAt(i)) >>> 0;
return FALLBACK_COLORS[hash % FALLBACK_COLORS.length]!;
}