feat(frontend): add primitive UI components (Icon, StatusPill, UnderlineStroke, StatCard, Tally, Field)
This commit is contained in:
24
frontend/src/lib/components/Field.svelte
Normal file
24
frontend/src/lib/components/Field.svelte
Normal file
@@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
const { label, value = '', mono = false, suffix, readonly = false } = $props<{
|
||||
label: string;
|
||||
value?: string;
|
||||
mono?: boolean;
|
||||
suffix?: string;
|
||||
readonly?: boolean;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<div style="display:flex;flex-direction:column;gap:3px">
|
||||
<span class="tiny" style="color:var(--ink-3)">{label}</span>
|
||||
<div style="display:flex;align-items:center;gap:6px">
|
||||
<input
|
||||
class="input"
|
||||
style={mono ? 'font-family:var(--mono);font-size:12px' : ''}
|
||||
value={value}
|
||||
readonly={readonly}
|
||||
/>
|
||||
{#if suffix}
|
||||
<span class="tiny" style="color:var(--ink-4)">{suffix}</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
52
frontend/src/lib/components/Icon.svelte
Normal file
52
frontend/src/lib/components/Icon.svelte
Normal file
@@ -0,0 +1,52 @@
|
||||
<script lang="ts">
|
||||
const { name, size = 14 } = $props<{
|
||||
name: 'check'|'x'|'lock'|'open'|'copy'|'edit'|'download'|'arrow'|'search'|'plus';
|
||||
size?: number;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
{#if name === 'check'}
|
||||
<svg width={size} height={size} viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2.5 7.5 L5.5 10.5 L11.5 3.5"/>
|
||||
</svg>
|
||||
{:else if name === 'x'}
|
||||
<svg width={size} height={size} viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round">
|
||||
<path d="M3 3 L9 9 M9 3 L3 9"/>
|
||||
</svg>
|
||||
{:else if name === 'lock'}
|
||||
<svg width={size} height={size} viewBox="0 0 13 13" fill="none" stroke="currentColor" stroke-width="1.3">
|
||||
<rect x="2.5" y="6" width="8" height="5.5" rx="1"/>
|
||||
<path d="M4.5 6 V4.5 a2 2 0 0 1 4 0 V6"/>
|
||||
</svg>
|
||||
{:else if name === 'open'}
|
||||
<svg width={size} height={size} viewBox="0 0 13 13" fill="none" stroke="currentColor" stroke-width="1.3">
|
||||
<rect x="2.5" y="6" width="8" height="5.5" rx="1"/>
|
||||
<path d="M4.5 6 V4.5 a2 2 0 0 1 4 0"/>
|
||||
</svg>
|
||||
{:else if name === 'copy'}
|
||||
<svg width={size} height={size} viewBox="0 0 13 13" fill="none" stroke="currentColor" stroke-width="1.2">
|
||||
<rect x="2" y="2" width="7" height="7" rx="1"/>
|
||||
<rect x="4.5" y="4.5" width="7" height="7" rx="1"/>
|
||||
</svg>
|
||||
{:else if name === 'edit'}
|
||||
<svg width={size} height={size} viewBox="0 0 13 13" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round">
|
||||
<path d="M2 11 L2 9 L9 2 L11 4 L4 11 Z"/>
|
||||
</svg>
|
||||
{:else if name === 'download'}
|
||||
<svg width={size} height={size} viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M7 2 V9 M3.5 6 L7 9 L10.5 6 M2.5 11.5 H11.5"/>
|
||||
</svg>
|
||||
{:else if name === 'arrow'}
|
||||
<svg width={size} height={size} viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3 6 H9 M6.5 3 L9 6 L6.5 9"/>
|
||||
</svg>
|
||||
{:else if name === 'search'}
|
||||
<svg width={size} height={size} viewBox="0 0 13 13" fill="none" stroke="currentColor" stroke-width="1.3">
|
||||
<circle cx="5.5" cy="5.5" r="3.5"/>
|
||||
<path d="M8 8 L11 11" stroke-linecap="round"/>
|
||||
</svg>
|
||||
{:else if name === 'plus'}
|
||||
<svg width={size} height={size} viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round">
|
||||
<path d="M6 2 V10 M2 6 H10"/>
|
||||
</svg>
|
||||
{/if}
|
||||
24
frontend/src/lib/components/StatCard.svelte
Normal file
24
frontend/src/lib/components/StatCard.svelte
Normal file
@@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
const { label, value, suffix, hint, accent } = $props<{
|
||||
label: string;
|
||||
value: string | number;
|
||||
suffix?: string;
|
||||
hint?: string;
|
||||
accent?: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<div class="card" style="padding: 16px 20px">
|
||||
<span class="eyebrow">{label}</span>
|
||||
<div style="display:flex;align-items:baseline;gap:6px;margin-top:8px">
|
||||
<span style="font-family:var(--serif);font-size:32px;font-weight:500;line-height:1;color:{accent || 'var(--ink)'}">
|
||||
{value}
|
||||
</span>
|
||||
{#if suffix}
|
||||
<span class="tiny" style="color:var(--ink-4)">{suffix}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#if hint}
|
||||
<div class="tiny" style="color:var(--ink-4);margin-top:4px">{hint}</div>
|
||||
{/if}
|
||||
</div>
|
||||
15
frontend/src/lib/components/StatusPill.svelte
Normal file
15
frontend/src/lib/components/StatusPill.svelte
Normal file
@@ -0,0 +1,15 @@
|
||||
<script lang="ts">
|
||||
const { status } = $props<{
|
||||
status: 'open' | 'closed' | 'locked' | 'present' | 'absent';
|
||||
}>();
|
||||
|
||||
const labels: Record<string, string> = {
|
||||
open: 'OFFEN',
|
||||
closed: 'GESCHL.',
|
||||
locked: 'GESPERRT',
|
||||
present: 'ANWESEND',
|
||||
absent: 'FEHLT',
|
||||
};
|
||||
</script>
|
||||
|
||||
<span class="pill {status}"><span class="dot"></span>{labels[status]}</span>
|
||||
21
frontend/src/lib/components/Tally.svelte
Normal file
21
frontend/src/lib/components/Tally.svelte
Normal file
@@ -0,0 +1,21 @@
|
||||
<script lang="ts">
|
||||
const { label, value, total, suffix, accent } = $props<{
|
||||
label: string;
|
||||
value: string | number;
|
||||
total?: number;
|
||||
suffix?: string;
|
||||
accent?: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<div style="display:flex;flex-direction:column;gap:2px">
|
||||
<span class="eyebrow">{label}</span>
|
||||
<span style="font-family:var(--serif);font-size:22px;font-weight:500;color:{accent || 'var(--ink)'}">
|
||||
{value}
|
||||
{#if total !== undefined}
|
||||
<span class="tiny" style="color:var(--ink-4)"> / {total}</span>
|
||||
{:else if suffix}
|
||||
<span class="tiny" style="color:var(--ink-4)"> {suffix}</span>
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
21
frontend/src/lib/components/UnderlineStroke.svelte
Normal file
21
frontend/src/lib/components/UnderlineStroke.svelte
Normal file
@@ -0,0 +1,21 @@
|
||||
<script lang="ts">
|
||||
const { width = 110, color = 'var(--accent)' } = $props<{
|
||||
width?: number;
|
||||
color?: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<svg
|
||||
width={width}
|
||||
height="8"
|
||||
viewBox="0 0 {width} 8"
|
||||
fill="none"
|
||||
style="display:block;margin-top:2px"
|
||||
>
|
||||
<path
|
||||
d="M 2 5 Q {width * 0.25} 1 {width * 0.5} 4 T {width - 2} 3"
|
||||
stroke={color}
|
||||
stroke-width="1.6"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
Reference in New Issue
Block a user