Initial commit: Add project files
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, useId } from 'vue'
|
||||
const props = defineProps<{ label: string; inputId?: string; required?: boolean; error?: string; help?: string }>()
|
||||
const generatedId = useId()
|
||||
const resolvedId = computed(() => props.inputId ?? `ks-field-${generatedId}`)
|
||||
const messageId = computed(() => props.error || props.help ? `${resolvedId.value}-message` : undefined)
|
||||
</script>
|
||||
<template>
|
||||
<div class="ks-field-shell" :data-invalid="Boolean(error) || undefined">
|
||||
<label :for="resolvedId">{{ label }} <span v-if="required" aria-hidden="true">*</span></label>
|
||||
<slot :input-id="resolvedId" :described-by="messageId" :invalid="Boolean(error)" />
|
||||
<small v-if="error || help" :id="messageId" :class="{ 'ks-danger-text': error }" :role="error ? 'alert' : undefined">{{ error ?? help }}</small>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.ks-field-shell{display:grid;gap:var(--ks-space-1)}
|
||||
label{font-weight:650} small{color:var(--ks-color-neutral-600)}
|
||||
</style>
|
||||
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { UiButtonType, UiSeverity } from '../adapter/contracts'
|
||||
import { useUiAdapter } from '../adapter/useUiAdapter'
|
||||
|
||||
withDefaults(defineProps<{ label?: string; severity?: UiSeverity; type?: UiButtonType; disabled?: boolean; loading?: boolean }>(), {
|
||||
severity: 'primary', type: 'button', disabled: false, loading: false
|
||||
})
|
||||
const emit = defineEmits<{ click: [event: MouseEvent] }>()
|
||||
const adapter = useUiAdapter()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="adapter.components.Button" v-bind="$props" @activate="emit('click', $event)"><slot /></component>
|
||||
</template>
|
||||
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, useId } from 'vue'
|
||||
import { useUiAdapter } from '../adapter/useUiAdapter'
|
||||
const props = defineProps<{ modelValue: boolean; label: string; inputId?: string; disabled?: boolean }>()
|
||||
const emit = defineEmits<{ 'update:modelValue': [value: boolean] }>()
|
||||
const adapter = useUiAdapter()
|
||||
const generatedId = useId()
|
||||
const resolvedId = computed(() => props.inputId ?? `ks-check-${generatedId}`)
|
||||
</script>
|
||||
<template><label class="ks-check" :for="resolvedId"><component :is="adapter.components.Checkbox" :input-id="resolvedId" :model-value="modelValue" :disabled="disabled" @update:model-value="emit('update:modelValue', $event)" /><span>{{ label }}</span></label></template>
|
||||
<style scoped>.ks-check { display: inline-flex; align-items: center; gap: var(--ks-space-2); cursor: pointer; }</style>
|
||||
@@ -0,0 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import KsButton from './KsButton.vue'
|
||||
export interface CommandBarAction { id: string; label: string; severity?: 'primary'|'secondary'|'success'|'info'|'warning'|'danger'; disabled?: boolean; busy?: boolean }
|
||||
defineProps<{ actions: readonly CommandBarAction[]; ariaLabel?: string }>()
|
||||
const emit = defineEmits<{ execute: [actionId: string] }>()
|
||||
</script>
|
||||
<template><nav class="ks-command-bar" :aria-label="ariaLabel ?? 'Page actions'"><KsButton v-for="action in actions" :key="action.id" :label="action.label" :severity="action.severity" :disabled="action.disabled" :loading="action.busy" @click="emit('execute', action.id)" /></nav></template>
|
||||
<style scoped>.ks-command-bar{display:flex;gap:var(--ks-space-2);flex-wrap:wrap;justify-content:flex-end}</style>
|
||||
@@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import type { VersionSet } from '../../contracts/versionSet'
|
||||
import EvidenceVersionSet from '../EvidenceVersionSet.vue'
|
||||
const props = defineProps<{
|
||||
title: string; asOf: string; datasetId: string; dataHash: string; modelVersion: string; configVersion: string;
|
||||
codeSha: string; contractVersion: string; projectionVersion?: string; watermark?: string;
|
||||
stale?: boolean; correlationId?: string
|
||||
}>()
|
||||
const versionSet = computed<VersionSet>(() => ({
|
||||
datasetId: props.datasetId,
|
||||
dataHash: props.dataHash,
|
||||
modelVersion: props.modelVersion,
|
||||
configVersion: props.configVersion,
|
||||
codeSha: props.codeSha,
|
||||
contractVersion: props.contractVersion
|
||||
}))
|
||||
</script>
|
||||
<template>
|
||||
<header class="ks-data-context" :data-stale="stale || undefined">
|
||||
<div><h1>{{ title }}</h1><p>As-of {{ asOf }} <strong v-if="stale">STALE</strong></p></div>
|
||||
<EvidenceVersionSet :value="versionSet" compact />
|
||||
<dl v-if="projectionVersion || watermark || correlationId">
|
||||
<template v-if="projectionVersion"><dt>Projection</dt><dd>{{ projectionVersion }}</dd></template>
|
||||
<template v-if="watermark"><dt>Watermark</dt><dd>{{ watermark }}</dd></template>
|
||||
<template v-if="correlationId"><dt>Correlation</dt><dd>{{ correlationId }}</dd></template>
|
||||
</dl>
|
||||
</header>
|
||||
</template>
|
||||
<style scoped>
|
||||
.ks-data-context{display:grid;gap:var(--ks-space-3);padding:var(--ks-space-4);border:1px solid var(--ks-color-neutral-300);border-radius:var(--ks-radius-md)}
|
||||
.ks-data-context[data-stale=true]{border-color:var(--ks-color-warning-500)} h1,p{margin:0} dl{display:flex;gap:var(--ks-space-3);margin:0;flex-wrap:wrap} dt{font-weight:700} dd{margin:0}
|
||||
</style>
|
||||
@@ -0,0 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import type { UiGridColumn } from '../adapter/contracts'
|
||||
import { useUiAdapter } from '../adapter/useUiAdapter'
|
||||
withDefaults(defineProps<{ rows: unknown[]; columns: UiGridColumn[]; loading?: boolean; height?: string; rowSelection?: 'single' | 'multiple' | 'none' }>(), { loading: false, height: '32rem', rowSelection: 'single' })
|
||||
const emit = defineEmits<{ rowSelected: [row: unknown] }>()
|
||||
const adapter = useUiAdapter()
|
||||
</script>
|
||||
<template><component :is="adapter.components.DataGrid" v-bind="$props" @row-selected="emit('rowSelected', $event)" /></template>
|
||||
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, useId } from 'vue'
|
||||
import { useUiAdapter } from '../adapter/useUiAdapter'
|
||||
const props = defineProps<{ modelValue: string | Date | null; label: string; inputId?: string; disabled?: boolean; required?: boolean; error?: string; help?: string; min?: Date; max?: Date }>()
|
||||
const emit = defineEmits<{ 'update:modelValue': [value: string | Date | null]; blur: [event: FocusEvent] }>()
|
||||
const adapter = useUiAdapter()
|
||||
const generatedId = useId()
|
||||
const resolvedId = computed(() => props.inputId ?? `ks-date-${generatedId}`)
|
||||
</script>
|
||||
<template><div class="ks-field"><label :for="resolvedId">{{ label }} <span v-if="required" aria-hidden="true">*</span></label><component :is="adapter.components.DateField" :input-id="resolvedId" :model-value="modelValue" :disabled="disabled" :invalid="Boolean(error)" :min="min" :max="max" :aria-describedby="error || help ? `${resolvedId}-message` : undefined" @update:model-value="emit('update:modelValue', $event)" @blur="emit('blur', $event)" /><small v-if="error || help" :id="`${resolvedId}-message`" :class="{ 'ks-danger-text': error }">{{ error ?? help }}</small></div></template>
|
||||
<style scoped>.ks-field{display:grid;gap:var(--ks-space-1)}label{font-weight:650}small{color:var(--ks-color-neutral-600)}</style>
|
||||
@@ -0,0 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useUiAdapter } from '../adapter/useUiAdapter'
|
||||
defineProps<{ visible: boolean; title: string; modal?: boolean; closable?: boolean }>()
|
||||
const emit = defineEmits<{ 'update:visible': [value: boolean] }>()
|
||||
const adapter = useUiAdapter()
|
||||
</script>
|
||||
<template><component :is="adapter.components.Dialog" v-bind="$props" @update:visible="emit('update:visible', $event)"><slot /><template #footer><slot name="footer" /></template></component></template>
|
||||
@@ -0,0 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import type { UiSeverity } from '../adapter/contracts'
|
||||
import { useUiAdapter } from '../adapter/useUiAdapter'
|
||||
withDefaults(defineProps<{ severity?: UiSeverity; title?: string; message: string; dismissible?: boolean }>(), { severity: 'info', dismissible: false })
|
||||
const emit = defineEmits<{ dismiss: [] }>()
|
||||
const adapter = useUiAdapter()
|
||||
</script>
|
||||
<template><component :is="adapter.components.InlineMessage" v-bind="$props" @dismiss="emit('dismiss')" /></template>
|
||||
@@ -0,0 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import type { UiSelectOption } from '../adapter/contracts'
|
||||
import { useUiAdapter } from '../adapter/useUiAdapter'
|
||||
withDefaults(defineProps<{ modelValue?: Array<string | number | boolean | null>; options: UiSelectOption[]; label?: string; disabled?: boolean; required?: boolean }>(), { modelValue: () => [] })
|
||||
const emit = defineEmits<{ 'update:modelValue': [value: Array<string | number | boolean | null>] }>()
|
||||
const adapter = useUiAdapter()
|
||||
</script>
|
||||
<template><component :is="adapter.components.MultiSelect" v-bind="$props" @update:model-value="emit('update:modelValue', $event)" /></template>
|
||||
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, useId } from 'vue'
|
||||
import { useUiAdapter } from '../adapter/useUiAdapter'
|
||||
const props = defineProps<{ modelValue: number | null; label: string; inputId?: string; disabled?: boolean; required?: boolean; error?: string; help?: string; min?: number; max?: number; minFractionDigits?: number; maxFractionDigits?: number }>()
|
||||
const emit = defineEmits<{ 'update:modelValue': [value: number | null]; blur: [event: FocusEvent] }>()
|
||||
const adapter = useUiAdapter()
|
||||
const generatedId = useId()
|
||||
const resolvedId = computed(() => props.inputId ?? `ks-number-${generatedId}`)
|
||||
</script>
|
||||
<template><div class="ks-field"><label :for="resolvedId">{{ label }} <span v-if="required" aria-hidden="true">*</span></label><component :is="adapter.components.NumberField" :input-id="resolvedId" :model-value="modelValue" :disabled="disabled" :invalid="Boolean(error)" :min="min" :max="max" :min-fraction-digits="minFractionDigits" :max-fraction-digits="maxFractionDigits" :aria-describedby="error || help ? `${resolvedId}-message` : undefined" @update:model-value="emit('update:modelValue', $event)" @blur="emit('blur', $event)" /><small v-if="error || help" :id="`${resolvedId}-message`" :class="{ 'ks-danger-text': error }">{{ error ?? help }}</small></div></template>
|
||||
<style scoped>.ks-field{display:grid;gap:var(--ks-space-1)}label{font-weight:650}small{color:var(--ks-color-neutral-600)}</style>
|
||||
@@ -0,0 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useUiAdapter } from '../adapter/useUiAdapter'
|
||||
withDefaults(defineProps<{ page: number; pageSize: number; total: number; pageSizes?: number[]; disabled?: boolean }>(), { pageSizes: () => [20, 50, 100], disabled: false })
|
||||
const emit = defineEmits<{ pageChange: [value: { page: number; pageSize: number }] }>()
|
||||
const adapter = useUiAdapter()
|
||||
</script>
|
||||
<template><component :is="adapter.components.Paginator" v-bind="$props" @page-change="emit('pageChange', $event)" /></template>
|
||||
@@ -0,0 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, useId } from 'vue'
|
||||
import type { UiSelectOption } from '../adapter/contracts'
|
||||
import { useUiAdapter } from '../adapter/useUiAdapter'
|
||||
const props = defineProps<{ modelValue: unknown; label: string; options: UiSelectOption[]; inputId?: string; disabled?: boolean; required?: boolean; error?: string; help?: string; placeholder?: string }>()
|
||||
const emit = defineEmits<{ 'update:modelValue': [value: unknown]; blur: [event: FocusEvent] }>()
|
||||
const adapter = useUiAdapter()
|
||||
const generatedId = useId()
|
||||
const resolvedId = computed(() => props.inputId ?? `ks-select-${generatedId}`)
|
||||
</script>
|
||||
<template>
|
||||
<div class="ks-field">
|
||||
<label :for="resolvedId">{{ label }} <span v-if="required" aria-hidden="true">*</span></label>
|
||||
<component :is="adapter.components.Select" :input-id="resolvedId" :model-value="modelValue" :options="options" :disabled="disabled" :invalid="Boolean(error)" :placeholder="placeholder" @update:model-value="emit('update:modelValue', $event)" @blur="emit('blur', $event)" />
|
||||
<small v-if="error || help" :class="{ 'ks-danger-text': error }">{{ error ?? help }}</small>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>.ks-field { display: grid; gap: var(--ks-space-1); } label { font-weight: 650; } small { color: var(--ks-color-neutral-600); }</style>
|
||||
@@ -0,0 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { UiSeverity } from '../adapter/contracts'
|
||||
import { useUiAdapter } from '../adapter/useUiAdapter'
|
||||
defineProps<{ value: string; severity?: UiSeverity; iconLabel?: string }>()
|
||||
const adapter = useUiAdapter()
|
||||
</script>
|
||||
<template><component :is="adapter.components.StatusTag" v-bind="$props" /></template>
|
||||
@@ -0,0 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import type { UiTabItem } from '../adapter/contracts'
|
||||
import { useUiAdapter } from '../adapter/useUiAdapter'
|
||||
withDefaults(defineProps<{ modelValue: string; items: UiTabItem[]; ariaLabel?: string }>(), { ariaLabel: '탭' })
|
||||
const emit = defineEmits<{ 'update:modelValue': [value: string] }>()
|
||||
const adapter = useUiAdapter()
|
||||
</script>
|
||||
<template><component :is="adapter.components.Tabs" v-bind="$props" @update:model-value="emit('update:modelValue', $event)"><template #default="slotProps"><slot :active-id="slotProps.activeId" /></template></component></template>
|
||||
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, useId } from 'vue'
|
||||
import { useUiAdapter } from '../adapter/useUiAdapter'
|
||||
const props = defineProps<{ modelValue: string; label: string; inputId?: string; disabled?: boolean; required?: boolean; error?: string; help?: string; rows?: number; placeholder?: string }>()
|
||||
const emit = defineEmits<{ 'update:modelValue': [value: string]; blur: [event: FocusEvent] }>()
|
||||
const adapter = useUiAdapter()
|
||||
const generatedId = useId()
|
||||
const resolvedId = computed(() => props.inputId ?? `ks-area-${generatedId}`)
|
||||
</script>
|
||||
<template>
|
||||
<div class="ks-field">
|
||||
<label :for="resolvedId">{{ label }} <span v-if="required" aria-hidden="true">*</span></label>
|
||||
<component :is="adapter.components.TextArea" :input-id="resolvedId" :model-value="modelValue" :disabled="disabled" :invalid="Boolean(error)" :rows="rows" :placeholder="placeholder" @update:model-value="emit('update:modelValue', $event)" @blur="emit('blur', $event)" />
|
||||
<small v-if="error || help" :class="{ 'ks-danger-text': error }">{{ error ?? help }}</small>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>.ks-field { display: grid; gap: var(--ks-space-1); } label { font-weight: 650; } small { color: var(--ks-color-neutral-600); }</style>
|
||||
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, useId } from 'vue'
|
||||
import { useUiAdapter } from '../adapter/useUiAdapter'
|
||||
|
||||
const props = defineProps<{ modelValue: string; label: string; inputId?: string; disabled?: boolean; required?: boolean; error?: string; help?: string; placeholder?: string }>()
|
||||
const emit = defineEmits<{ 'update:modelValue': [value: string]; blur: [event: FocusEvent] }>()
|
||||
const adapter = useUiAdapter()
|
||||
const generatedId = useId()
|
||||
const resolvedId = computed(() => props.inputId ?? `ks-field-${generatedId}`)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ks-field">
|
||||
<label :for="resolvedId">{{ label }} <span v-if="required" aria-hidden="true">*</span></label>
|
||||
<component
|
||||
:is="adapter.components.TextField"
|
||||
:input-id="resolvedId"
|
||||
:model-value="modelValue"
|
||||
:disabled="disabled"
|
||||
:invalid="Boolean(error)"
|
||||
:placeholder="placeholder"
|
||||
:aria-describedby="error || help ? `${resolvedId}-message` : undefined"
|
||||
@update:model-value="emit('update:modelValue', $event)"
|
||||
@blur="emit('blur', $event)"
|
||||
/>
|
||||
<small v-if="error || help" :id="`${resolvedId}-message`" :class="{ 'ks-danger-text': error }">{{ error ?? help }}</small>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ks-field { display: grid; gap: var(--ks-space-1); }
|
||||
label { font-weight: 650; }
|
||||
small { color: var(--ks-color-neutral-600); }
|
||||
</style>
|
||||
@@ -0,0 +1,17 @@
|
||||
export { default as KsButton } from './KsButton.vue'
|
||||
export { default as KsTextField } from './KsTextField.vue'
|
||||
export { default as KsTextArea } from './KsTextArea.vue'
|
||||
export { default as KsSelect } from './KsSelect.vue'
|
||||
export { default as KsMultiSelect } from './KsMultiSelect.vue'
|
||||
export { default as KsCheckbox } from './KsCheckbox.vue'
|
||||
export { default as KsDateField } from './KsDateField.vue'
|
||||
export { default as KsNumberField } from './KsNumberField.vue'
|
||||
export { default as KsDialog } from './KsDialog.vue'
|
||||
export { default as KsStatusTag } from './KsStatusTag.vue'
|
||||
export { default as KsInlineMessage } from './KsInlineMessage.vue'
|
||||
export { default as KsPaginator } from './KsPaginator.vue'
|
||||
export { default as KsTabs } from './KsTabs.vue'
|
||||
export { default as KsDataGrid } from './KsDataGrid.vue'
|
||||
export { default as FieldShell } from './FieldShell.vue'
|
||||
export { default as KsDataContextHeader } from './KsDataContextHeader.vue'
|
||||
export { default as KsCommandBar } from './KsCommandBar.vue'
|
||||
Reference in New Issue
Block a user