@prsm/mono-components 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +13 -0
- package/.lore +83 -0
- package/histoire.config.js +43 -0
- package/package.json +39 -0
- package/postcss.config.js +6 -0
- package/src/components/Badge.vue +36 -0
- package/src/components/Button.vue +44 -0
- package/src/components/Checkbox.vue +51 -0
- package/src/components/CheckboxCards.vue +61 -0
- package/src/components/CodeEditor.vue +299 -0
- package/src/components/Collapsible.vue +69 -0
- package/src/components/CollapsibleGroup.vue +38 -0
- package/src/components/Combobox.vue +179 -0
- package/src/components/ContextMenu.vue +65 -0
- package/src/components/ContextMenuPanel.vue +115 -0
- package/src/components/DataTable.vue +326 -0
- package/src/components/Dropdown.vue +127 -0
- package/src/components/GhostInput.vue +29 -0
- package/src/components/Input.vue +23 -0
- package/src/components/KeyValue.vue +149 -0
- package/src/components/LabeledTextarea.vue +64 -0
- package/src/components/LabeledTextareaGroup.vue +14 -0
- package/src/components/Mention.vue +79 -0
- package/src/components/Modal.vue +109 -0
- package/src/components/MultiCombobox.vue +209 -0
- package/src/components/NavTree.vue +98 -0
- package/src/components/NumberInput.vue +128 -0
- package/src/components/PopConfirm.vue +94 -0
- package/src/components/Popover.vue +53 -0
- package/src/components/RadioCards.vue +37 -0
- package/src/components/RadioGroup.vue +57 -0
- package/src/components/RangeSlider.vue +165 -0
- package/src/components/ScrollBox.vue +78 -0
- package/src/components/SectionHeader.vue +18 -0
- package/src/components/Select.vue +187 -0
- package/src/components/Switch.vue +85 -0
- package/src/components/Tabs.vue +34 -0
- package/src/components/TagInput.vue +80 -0
- package/src/components/Textarea.vue +97 -0
- package/src/components/ToastContainer.vue +104 -0
- package/src/components/ToggleButtons.vue +45 -0
- package/src/components/ToggleGroup.vue +30 -0
- package/src/components/Tooltip.vue +56 -0
- package/src/components/Tree.vue +188 -0
- package/src/composables/toast.js +54 -0
- package/src/composables/useClickOutside.js +23 -0
- package/src/composables/useMention.js +291 -0
- package/src/composables/usePointerDrag.js +39 -0
- package/src/histoire-setup.js +1 -0
- package/src/index.js +43 -0
- package/src/style.css +96 -0
- package/stories/Badge.story.vue +24 -0
- package/stories/Button.story.vue +45 -0
- package/stories/Checkbox.story.vue +31 -0
- package/stories/CheckboxCards.story.vue +51 -0
- package/stories/CodeEditor.story.vue +71 -0
- package/stories/Collapsible.story.vue +84 -0
- package/stories/Combobox.story.vue +44 -0
- package/stories/ContextMenu.story.vue +59 -0
- package/stories/DataTable.story.vue +185 -0
- package/stories/Dropdown.story.vue +49 -0
- package/stories/GhostInput.story.vue +24 -0
- package/stories/Input.story.vue +23 -0
- package/stories/KeyValue.story.vue +104 -0
- package/stories/LabeledTextarea.story.vue +44 -0
- package/stories/Mention.story.vue +166 -0
- package/stories/Modal.story.vue +86 -0
- package/stories/MultiCombobox.story.vue +76 -0
- package/stories/NavTree.story.vue +184 -0
- package/stories/NumberInput.story.vue +31 -0
- package/stories/Overview.story.vue +85 -0
- package/stories/PopConfirm.story.vue +39 -0
- package/stories/RadioCards.story.vue +66 -0
- package/stories/RadioGroup.story.vue +52 -0
- package/stories/RangeSlider.story.vue +75 -0
- package/stories/ScrollBox.story.vue +54 -0
- package/stories/SectionHeader.story.vue +22 -0
- package/stories/Select.story.vue +34 -0
- package/stories/Switch.story.vue +42 -0
- package/stories/Tabs.story.vue +34 -0
- package/stories/TagInput.story.vue +54 -0
- package/stories/Textarea.story.vue +28 -0
- package/stories/Toast.story.vue +28 -0
- package/stories/ToggleButtons.story.vue +57 -0
- package/stories/ToggleGroup.story.vue +34 -0
- package/stories/Tooltip.story.vue +55 -0
- package/stories/Tree.story.vue +115 -0
- package/tailwind.config.js +9 -0
- package/tailwind.preset.js +79 -0
- package/vite.config.js +6 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, reactive } from "vue"
|
|
3
|
+
import { logEvent } from "histoire/client"
|
|
4
|
+
import DataTable from "../src/components/DataTable.vue"
|
|
5
|
+
import Badge from "../src/components/Badge.vue"
|
|
6
|
+
import Dropdown from "../src/components/Dropdown.vue"
|
|
7
|
+
|
|
8
|
+
function makeUsers(count) {
|
|
9
|
+
const names = ["Alice Chen", "Bob Smith", "Carol Wu", "Dave Kim", "Eve Park", "Frank Lee", "Grace Liu", "Hank Zhao", "Iris Wang", "Jack Yang", "Kate Tan", "Leo Ng", "Mia Choi", "Nate Lin", "Olga Sun"]
|
|
10
|
+
const roles = ["admin", "editor", "viewer"]
|
|
11
|
+
const statuses = ["active", "inactive", "pending"]
|
|
12
|
+
return Array.from({ length: count }, (_, i) => ({
|
|
13
|
+
id: i + 1,
|
|
14
|
+
name: names[i % names.length],
|
|
15
|
+
email: `${names[i % names.length].toLowerCase().replace(" ", ".")}@example.com`,
|
|
16
|
+
role: roles[i % roles.length],
|
|
17
|
+
status: statuses[i % statuses.length],
|
|
18
|
+
active: i % 3 !== 2,
|
|
19
|
+
score: Math.floor(Math.random() * 100)
|
|
20
|
+
}))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const users = reactive(makeUsers(47))
|
|
24
|
+
const loading = ref(false)
|
|
25
|
+
const lastEdit = ref(null)
|
|
26
|
+
const lastAction = ref(null)
|
|
27
|
+
|
|
28
|
+
const columns = [
|
|
29
|
+
{ key: "id", label: "#", width: "50px", sortable: true },
|
|
30
|
+
{ key: "name", label: "Name", sortable: true, editable: true },
|
|
31
|
+
{ key: "email", label: "Email", sortable: true },
|
|
32
|
+
{ key: "role", label: "Role", type: "enum", options: ["admin", "editor", "viewer"], editable: true },
|
|
33
|
+
{ key: "status", label: "Status", sortable: true },
|
|
34
|
+
{ key: "active", label: "Active", type: "boolean", editable: true, width: "70px" },
|
|
35
|
+
{ key: "score", label: "Score", type: "number", sortable: true, editable: true, width: "80px" }
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
const badgeColumns = [
|
|
39
|
+
{ key: "id", label: "#", width: "50px", sortable: true },
|
|
40
|
+
{ key: "name", label: "Name", sortable: true },
|
|
41
|
+
{ key: "email", label: "Email", sortable: true },
|
|
42
|
+
{ key: "status", label: "Status", sortable: true },
|
|
43
|
+
{ key: "score", label: "Score", sortable: true, width: "80px" }
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
const actionColumns = [
|
|
47
|
+
{ key: "id", label: "#", width: "50px" },
|
|
48
|
+
{ key: "name", label: "Name", sortable: true },
|
|
49
|
+
{ key: "email", label: "Email", sortable: true },
|
|
50
|
+
{ key: "status", label: "Status", sortable: true },
|
|
51
|
+
{ key: "actions", label: "", width: "40px" }
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
const rowActions = [
|
|
55
|
+
{ value: "edit", label: "Edit", icon: "material-symbols:edit-outline" },
|
|
56
|
+
{ value: "duplicate", label: "Duplicate", icon: "material-symbols:content-copy" },
|
|
57
|
+
{ value: "delete", label: "Delete", icon: "material-symbols:delete-outline" }
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
function handleRowUpdate({ key, column, value }) {
|
|
61
|
+
const row = users.find(r => r.id === key)
|
|
62
|
+
if (row) row[column] = value
|
|
63
|
+
lastEdit.value = { key, column, value }
|
|
64
|
+
logEvent("update:row", { key, column, value })
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function handleAction(option, row) {
|
|
68
|
+
lastAction.value = { action: option.value, row: row.name, id: row.id }
|
|
69
|
+
logEvent("action", { action: option.value, row: row.name, id: row.id })
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function simulateLoading() {
|
|
73
|
+
loading.value = true
|
|
74
|
+
setTimeout(() => { loading.value = false }, 2000)
|
|
75
|
+
}
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<template>
|
|
79
|
+
<Story title="DataTable">
|
|
80
|
+
<Variant title="Full Featured">
|
|
81
|
+
<div class="max-w-4xl">
|
|
82
|
+
<DataTable
|
|
83
|
+
:columns="columns"
|
|
84
|
+
:rows="users"
|
|
85
|
+
:page-size="10"
|
|
86
|
+
filterable
|
|
87
|
+
striped
|
|
88
|
+
@update:row="handleRowUpdate"
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
91
|
+
<template #controls>
|
|
92
|
+
<div class="text-xs text-fg-2 p-2">
|
|
93
|
+
<template v-if="lastEdit">
|
|
94
|
+
last edit: row {{ lastEdit.key }} . {{ lastEdit.column }} = {{ lastEdit.value }}
|
|
95
|
+
</template>
|
|
96
|
+
<template v-else>click an underlined cell to edit, click anywhere in a boolean cell to toggle</template>
|
|
97
|
+
</div>
|
|
98
|
+
</template>
|
|
99
|
+
</Variant>
|
|
100
|
+
<Variant title="Row Actions (Dropdown)">
|
|
101
|
+
<div class="max-w-3xl">
|
|
102
|
+
<DataTable
|
|
103
|
+
:columns="actionColumns"
|
|
104
|
+
:rows="users"
|
|
105
|
+
:page-size="8"
|
|
106
|
+
filterable
|
|
107
|
+
>
|
|
108
|
+
<template #cell-status="{ value }">
|
|
109
|
+
<Badge :variant="value === 'active' ? 'success' : value === 'pending' ? 'warning' : 'neutral'">
|
|
110
|
+
{{ value }}
|
|
111
|
+
</Badge>
|
|
112
|
+
</template>
|
|
113
|
+
<template #cell-actions="{ row }">
|
|
114
|
+
<Dropdown
|
|
115
|
+
inline
|
|
116
|
+
icon="material-symbols:more-horiz"
|
|
117
|
+
:options="rowActions"
|
|
118
|
+
@select="opt => handleAction(opt, row)"
|
|
119
|
+
/>
|
|
120
|
+
</template>
|
|
121
|
+
</DataTable>
|
|
122
|
+
</div>
|
|
123
|
+
<template #controls>
|
|
124
|
+
<div class="text-xs text-fg-2 p-2">
|
|
125
|
+
<template v-if="lastAction">
|
|
126
|
+
{{ lastAction.action }} on {{ lastAction.row }} (#{{ lastAction.id }})
|
|
127
|
+
</template>
|
|
128
|
+
<template v-else>click the ... icon to see row actions</template>
|
|
129
|
+
</div>
|
|
130
|
+
</template>
|
|
131
|
+
</Variant>
|
|
132
|
+
<Variant title="Custom Cell Slot (Badge)">
|
|
133
|
+
<div class="max-w-3xl">
|
|
134
|
+
<DataTable
|
|
135
|
+
:columns="badgeColumns"
|
|
136
|
+
:rows="users"
|
|
137
|
+
:page-size="8"
|
|
138
|
+
>
|
|
139
|
+
<template #cell-status="{ value }">
|
|
140
|
+
<Badge :variant="value === 'active' ? 'success' : value === 'pending' ? 'warning' : 'neutral'">
|
|
141
|
+
{{ value }}
|
|
142
|
+
</Badge>
|
|
143
|
+
</template>
|
|
144
|
+
</DataTable>
|
|
145
|
+
</div>
|
|
146
|
+
</Variant>
|
|
147
|
+
<Variant title="Loading">
|
|
148
|
+
<div class="max-w-3xl flex flex-col gap-2">
|
|
149
|
+
<button
|
|
150
|
+
class="self-start px-2 py-1 text-base bg-2 border border-line rounded-sm font-mono cursor-pointer hover:bg-3"
|
|
151
|
+
@click="simulateLoading"
|
|
152
|
+
>
|
|
153
|
+
simulate loading
|
|
154
|
+
</button>
|
|
155
|
+
<DataTable
|
|
156
|
+
:columns="badgeColumns"
|
|
157
|
+
:rows="loading ? [] : users"
|
|
158
|
+
:loading="loading"
|
|
159
|
+
:page-size="5"
|
|
160
|
+
/>
|
|
161
|
+
</div>
|
|
162
|
+
</Variant>
|
|
163
|
+
<Variant title="Empty State">
|
|
164
|
+
<div class="max-w-3xl">
|
|
165
|
+
<DataTable
|
|
166
|
+
:columns="badgeColumns"
|
|
167
|
+
:rows="[]"
|
|
168
|
+
:page-size="5"
|
|
169
|
+
/>
|
|
170
|
+
</div>
|
|
171
|
+
</Variant>
|
|
172
|
+
<Variant title="Compact (5 per page)">
|
|
173
|
+
<div class="max-w-3xl">
|
|
174
|
+
<DataTable
|
|
175
|
+
:columns="columns"
|
|
176
|
+
:rows="users"
|
|
177
|
+
:page-size="5"
|
|
178
|
+
filterable
|
|
179
|
+
filter-placeholder="find user..."
|
|
180
|
+
@update:row="handleRowUpdate"
|
|
181
|
+
/>
|
|
182
|
+
</div>
|
|
183
|
+
</Variant>
|
|
184
|
+
</Story>
|
|
185
|
+
</template>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref } from "vue"
|
|
3
|
+
import Dropdown from "../src/components/Dropdown.vue"
|
|
4
|
+
|
|
5
|
+
const selected = ref("grid")
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<Story title="Dropdown">
|
|
10
|
+
<Variant title="With Icon and Label">
|
|
11
|
+
<Dropdown
|
|
12
|
+
v-model="selected"
|
|
13
|
+
icon="material-symbols:view-module"
|
|
14
|
+
label="View"
|
|
15
|
+
header="Layout"
|
|
16
|
+
:options="[
|
|
17
|
+
{ value: 'grid', label: 'Grid', icon: 'material-symbols:grid-view' },
|
|
18
|
+
{ value: 'list', label: 'List', icon: 'material-symbols:view-list' },
|
|
19
|
+
{ value: 'board', label: 'Board', icon: 'material-symbols:view-kanban' }
|
|
20
|
+
]"
|
|
21
|
+
/>
|
|
22
|
+
<template #controls>
|
|
23
|
+
<div class="text-xs text-fg-2 p-2">value: {{ selected }}</div>
|
|
24
|
+
</template>
|
|
25
|
+
</Variant>
|
|
26
|
+
<Variant title="With Meta">
|
|
27
|
+
<Dropdown
|
|
28
|
+
v-model="selected"
|
|
29
|
+
label="Sort"
|
|
30
|
+
:options="[
|
|
31
|
+
{ value: 'name', label: 'Name', meta: 'A-Z' },
|
|
32
|
+
{ value: 'date', label: 'Date', meta: 'newest' },
|
|
33
|
+
{ value: 'size', label: 'Size', meta: 'largest' }
|
|
34
|
+
]"
|
|
35
|
+
/>
|
|
36
|
+
</Variant>
|
|
37
|
+
<Variant title="With Action">
|
|
38
|
+
<Dropdown
|
|
39
|
+
v-model="selected"
|
|
40
|
+
label="File"
|
|
41
|
+
:options="[
|
|
42
|
+
{ value: 'save', label: 'Save' },
|
|
43
|
+
{ value: 'export', label: 'Export' },
|
|
44
|
+
{ value: 'delete', label: 'Delete', action: () => alert('delete!') }
|
|
45
|
+
]"
|
|
46
|
+
/>
|
|
47
|
+
</Variant>
|
|
48
|
+
</Story>
|
|
49
|
+
</template>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref } from "vue"
|
|
3
|
+
import GhostInput from "../src/components/GhostInput.vue"
|
|
4
|
+
|
|
5
|
+
const title = ref("Untitled Project")
|
|
6
|
+
const description = ref("")
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<Story title="GhostInput">
|
|
11
|
+
<Variant title="Large (Title)">
|
|
12
|
+
<GhostInput v-model="title" size="lg" placeholder="Untitled..." />
|
|
13
|
+
</Variant>
|
|
14
|
+
<Variant title="Base (Description)">
|
|
15
|
+
<GhostInput v-model="description" placeholder="Add a description..." />
|
|
16
|
+
</Variant>
|
|
17
|
+
<Variant title="Stacked">
|
|
18
|
+
<div class="flex flex-col gap-1">
|
|
19
|
+
<GhostInput v-model="title" size="lg" placeholder="Untitled..." />
|
|
20
|
+
<GhostInput v-model="description" placeholder="Add a description..." />
|
|
21
|
+
</div>
|
|
22
|
+
</Variant>
|
|
23
|
+
</Story>
|
|
24
|
+
</template>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref } from "vue"
|
|
3
|
+
import Input from "../src/components/Input.vue"
|
|
4
|
+
|
|
5
|
+
const value = ref("")
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<Story title="Input">
|
|
10
|
+
<Variant title="Default">
|
|
11
|
+
<Input v-model="value" placeholder="type something..." />
|
|
12
|
+
<template #controls>
|
|
13
|
+
<div class="text-xs text-fg-2 p-2">value: "{{ value }}"</div>
|
|
14
|
+
</template>
|
|
15
|
+
</Variant>
|
|
16
|
+
<Variant title="With Value">
|
|
17
|
+
<Input model-value="hello world" />
|
|
18
|
+
</Variant>
|
|
19
|
+
<Variant title="Password">
|
|
20
|
+
<Input type="password" placeholder="password..." />
|
|
21
|
+
</Variant>
|
|
22
|
+
</Story>
|
|
23
|
+
</template>
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { reactive } from "vue"
|
|
3
|
+
import { logEvent } from "histoire/client"
|
|
4
|
+
import KeyValue from "../src/components/KeyValue.vue"
|
|
5
|
+
import Badge from "../src/components/Badge.vue"
|
|
6
|
+
|
|
7
|
+
const user = reactive({
|
|
8
|
+
name: "Alice Chen",
|
|
9
|
+
email: "alice@example.com",
|
|
10
|
+
role: "admin",
|
|
11
|
+
active: true,
|
|
12
|
+
score: 92,
|
|
13
|
+
created: "2024-01-15",
|
|
14
|
+
lastLogin: "2025-05-10 14:32"
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const userFields = [
|
|
18
|
+
{ key: "name", label: "Name", editable: true },
|
|
19
|
+
{ key: "email", label: "Email", editable: true },
|
|
20
|
+
{ key: "role", label: "Role", type: "enum", options: ["admin", "editor", "viewer"], editable: true },
|
|
21
|
+
{ key: "active", label: "Active", type: "boolean", editable: true },
|
|
22
|
+
{ key: "score", label: "Score", type: "number", editable: true },
|
|
23
|
+
{ key: "created", label: "Created" },
|
|
24
|
+
{ key: "lastLogin", label: "Last login" }
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
const config = reactive({
|
|
28
|
+
host: "localhost",
|
|
29
|
+
port: 5432,
|
|
30
|
+
database: "app_production",
|
|
31
|
+
ssl: true,
|
|
32
|
+
poolSize: 20,
|
|
33
|
+
timeout: 30000
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const configFields = [
|
|
37
|
+
{ key: "host", label: "Host", editable: true },
|
|
38
|
+
{ key: "port", label: "Port", type: "number", editable: true },
|
|
39
|
+
{ key: "database", label: "Database", editable: true },
|
|
40
|
+
{ key: "ssl", label: "SSL", type: "boolean", editable: true },
|
|
41
|
+
{ key: "poolSize", label: "Pool size", type: "number", editable: true },
|
|
42
|
+
{ key: "timeout", label: "Timeout (ms)", type: "number", editable: true }
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
const server = reactive({
|
|
46
|
+
status: "active",
|
|
47
|
+
uptime: "14d 6h 23m",
|
|
48
|
+
cpu: "23%",
|
|
49
|
+
memory: "1.2 GB / 4 GB",
|
|
50
|
+
requests: "12,847",
|
|
51
|
+
errors: "3"
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const serverFields = [
|
|
55
|
+
{ key: "status", label: "Status" },
|
|
56
|
+
{ key: "uptime", label: "Uptime" },
|
|
57
|
+
{ key: "cpu", label: "CPU" },
|
|
58
|
+
{ key: "memory", label: "Memory" },
|
|
59
|
+
{ key: "requests", label: "Requests" },
|
|
60
|
+
{ key: "errors", label: "Errors" }
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
function handleUpdate(obj, { key, value }) {
|
|
64
|
+
obj[key] = value
|
|
65
|
+
logEvent("update", { key, value })
|
|
66
|
+
}
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<template>
|
|
70
|
+
<Story title="KeyValue">
|
|
71
|
+
<Variant title="Editable User Profile">
|
|
72
|
+
<div class="max-w-sm border border-line rounded-sm overflow-hidden">
|
|
73
|
+
<KeyValue
|
|
74
|
+
:fields="userFields"
|
|
75
|
+
:data="user"
|
|
76
|
+
@update="e => handleUpdate(user, e)"
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
</Variant>
|
|
80
|
+
<Variant title="Database Config">
|
|
81
|
+
<div class="max-w-sm border border-line rounded-sm overflow-hidden">
|
|
82
|
+
<KeyValue
|
|
83
|
+
:fields="configFields"
|
|
84
|
+
:data="config"
|
|
85
|
+
label-width="100px"
|
|
86
|
+
@update="e => handleUpdate(config, e)"
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
</Variant>
|
|
90
|
+
<Variant title="Read-only with Custom Slot">
|
|
91
|
+
<div class="max-w-sm border border-line rounded-sm overflow-hidden">
|
|
92
|
+
<KeyValue
|
|
93
|
+
:fields="serverFields"
|
|
94
|
+
:data="server"
|
|
95
|
+
label-width="80px"
|
|
96
|
+
>
|
|
97
|
+
<template #value-status="{ value }">
|
|
98
|
+
<Badge :variant="value === 'active' ? 'success' : 'error'">{{ value }}</Badge>
|
|
99
|
+
</template>
|
|
100
|
+
</KeyValue>
|
|
101
|
+
</div>
|
|
102
|
+
</Variant>
|
|
103
|
+
</Story>
|
|
104
|
+
</template>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref } from "vue"
|
|
3
|
+
import LabeledTextarea from "../src/components/LabeledTextarea.vue"
|
|
4
|
+
import LabeledTextareaGroup from "../src/components/LabeledTextareaGroup.vue"
|
|
5
|
+
|
|
6
|
+
const system = ref("high-level instructions for the model...")
|
|
7
|
+
const prompt = ref("Based on the retrieved context, extract:\n- summary: 2-3 sentence overview\n- entities: all named entities (people, places, organizations)\n- sentiment: overall sentiment (positive, neutral, or negative)\n\nBe thorough but concise.")
|
|
8
|
+
const empty = ref("")
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<Story title="LabeledTextarea">
|
|
13
|
+
<Variant title="Standalone">
|
|
14
|
+
<LabeledTextarea v-model="system" label="System Instructions" />
|
|
15
|
+
</Variant>
|
|
16
|
+
<Variant title="Standalone (placeholder)">
|
|
17
|
+
<LabeledTextarea v-model="empty" label="Notes" placeholder="Write something..." />
|
|
18
|
+
</Variant>
|
|
19
|
+
<Variant title="Grouped">
|
|
20
|
+
<LabeledTextareaGroup>
|
|
21
|
+
<LabeledTextarea v-model="system" label="System Instructions" auto-grow />
|
|
22
|
+
<LabeledTextarea v-model="prompt" label="Per-Iteration Prompt" auto-grow />
|
|
23
|
+
</LabeledTextareaGroup>
|
|
24
|
+
</Variant>
|
|
25
|
+
<Variant title="With Icons">
|
|
26
|
+
<LabeledTextarea v-model="system" label="System Instructions" icon="material-symbols:settings" />
|
|
27
|
+
</Variant>
|
|
28
|
+
<Variant title="Grouped with Icons">
|
|
29
|
+
<LabeledTextareaGroup>
|
|
30
|
+
<LabeledTextarea v-model="system" label="System Instructions" icon="material-symbols:settings" auto-grow />
|
|
31
|
+
<LabeledTextarea v-model="prompt" label="Per-Iteration Prompt" icon="material-symbols:repeat" auto-grow />
|
|
32
|
+
</LabeledTextareaGroup>
|
|
33
|
+
</Variant>
|
|
34
|
+
<Variant title="Grouped with Footer">
|
|
35
|
+
<LabeledTextareaGroup>
|
|
36
|
+
<LabeledTextarea v-model="system" label="System Instructions" icon="material-symbols:settings" auto-grow />
|
|
37
|
+
<LabeledTextarea v-model="prompt" label="Per-Iteration Prompt" icon="material-symbols:repeat" auto-grow />
|
|
38
|
+
<template #footer>
|
|
39
|
+
<span class="text-xs text-success">bindings valid</span>
|
|
40
|
+
</template>
|
|
41
|
+
</LabeledTextareaGroup>
|
|
42
|
+
</Variant>
|
|
43
|
+
</Story>
|
|
44
|
+
</template>
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref } from "vue"
|
|
3
|
+
import Mention from "../src/components/Mention.vue"
|
|
4
|
+
import { useMention } from "../src/composables/useMention.js"
|
|
5
|
+
|
|
6
|
+
const text1 = ref("")
|
|
7
|
+
const text2 = ref("")
|
|
8
|
+
const text3 = ref("")
|
|
9
|
+
const lastAction = ref(null)
|
|
10
|
+
|
|
11
|
+
const users = [
|
|
12
|
+
{ value: "alice", label: "Alice" },
|
|
13
|
+
{ value: "bob", label: "Bob" },
|
|
14
|
+
{ value: "carol", label: "Carol" },
|
|
15
|
+
{ value: "dave", label: "Dave" },
|
|
16
|
+
{ value: "eve", label: "Eve" },
|
|
17
|
+
{ value: "frank", label: "Frank" },
|
|
18
|
+
{ value: "grace", label: "Grace" },
|
|
19
|
+
{ value: "heidi", label: "Heidi" },
|
|
20
|
+
{ value: "ivan", label: "Ivan" },
|
|
21
|
+
{ value: "judy", label: "Judy" },
|
|
22
|
+
{ value: "karl", label: "Karl" },
|
|
23
|
+
{ value: "liam", label: "Liam" },
|
|
24
|
+
{ value: "mallory", label: "Mallory" },
|
|
25
|
+
{ value: "nina", label: "Nina" },
|
|
26
|
+
{ value: "oscar", label: "Oscar" },
|
|
27
|
+
{ value: "peggy", label: "Peggy" },
|
|
28
|
+
{ value: "quinn", label: "Quinn" },
|
|
29
|
+
{ value: "rupert", label: "Rupert" },
|
|
30
|
+
{ value: "sybil", label: "Sybil" },
|
|
31
|
+
{ value: "trent", label: "Trent" }
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
const commands = [
|
|
35
|
+
{ value: "help", label: "help" },
|
|
36
|
+
{ value: "clear", label: "clear" },
|
|
37
|
+
{ value: "export", label: "export" },
|
|
38
|
+
{ value: "import", label: "import" },
|
|
39
|
+
{ value: "settings", label: "settings" }
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
const actionCommands = [
|
|
43
|
+
{ value: "bold", label: "bold", action: (item) => lastAction.value = item.label },
|
|
44
|
+
{ value: "italic", label: "italic", action: (item) => lastAction.value = item.label },
|
|
45
|
+
{ value: "code", label: "code", action: (item) => lastAction.value = item.label },
|
|
46
|
+
{ value: "link", label: "link", action: (item) => lastAction.value = item.label },
|
|
47
|
+
{ value: "image", label: "image", action: (item) => lastAction.value = item.label }
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
const mention1 = useMention([
|
|
51
|
+
{ key: "@", items: users }
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
const mention2 = useMention([
|
|
55
|
+
{
|
|
56
|
+
key: "@",
|
|
57
|
+
onReveal: () => new Promise(r => setTimeout(() => r(users), 500))
|
|
58
|
+
},
|
|
59
|
+
{ key: "/", items: commands }
|
|
60
|
+
])
|
|
61
|
+
|
|
62
|
+
const mention3 = useMention([
|
|
63
|
+
{ key: "@", items: users },
|
|
64
|
+
{ key: "/", action: true, items: actionCommands }
|
|
65
|
+
])
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<template>
|
|
69
|
+
<Story title="Mention">
|
|
70
|
+
<Variant title="Static @mentions">
|
|
71
|
+
<div class="w-[400px]">
|
|
72
|
+
<textarea
|
|
73
|
+
v-model="text1"
|
|
74
|
+
class="w-full h-24 px-2 py-1 bg-0 border border-line rounded-sm font-mono text-base text-fg-0 outline-none focus:border-accent resize-none"
|
|
75
|
+
placeholder="type @ to mention a user..."
|
|
76
|
+
v-on="mention1.handlers"
|
|
77
|
+
/>
|
|
78
|
+
<Mention
|
|
79
|
+
:open="mention1.open.value"
|
|
80
|
+
:items="mention1.filtered.value"
|
|
81
|
+
:active-index="mention1.activeIndex.value"
|
|
82
|
+
:loading="mention1.loading.value"
|
|
83
|
+
:anchor="mention1.virtualAnchor.value"
|
|
84
|
+
@select="mention1.confirm($event)"
|
|
85
|
+
@hover="mention1.setActiveIndex($event)"
|
|
86
|
+
/>
|
|
87
|
+
</div>
|
|
88
|
+
<template #controls>
|
|
89
|
+
<div class="text-xs text-fg-2 p-2">
|
|
90
|
+
<div>value: {{ text1 || '(empty)' }}</div>
|
|
91
|
+
</div>
|
|
92
|
+
</template>
|
|
93
|
+
</Variant>
|
|
94
|
+
|
|
95
|
+
<Variant title="Multiple triggers (@ async + / static)">
|
|
96
|
+
<div class="w-[400px]">
|
|
97
|
+
<textarea
|
|
98
|
+
v-model="text2"
|
|
99
|
+
class="w-full h-24 px-2 py-1 bg-0 border border-line rounded-sm font-mono text-base text-fg-0 outline-none focus:border-accent resize-none"
|
|
100
|
+
placeholder="type @ for users (async), / for commands..."
|
|
101
|
+
v-on="mention2.handlers"
|
|
102
|
+
/>
|
|
103
|
+
<Mention
|
|
104
|
+
:open="mention2.open.value"
|
|
105
|
+
:items="mention2.filtered.value"
|
|
106
|
+
:active-index="mention2.activeIndex.value"
|
|
107
|
+
:loading="mention2.loading.value"
|
|
108
|
+
:anchor="mention2.virtualAnchor.value"
|
|
109
|
+
@select="mention2.confirm($event)"
|
|
110
|
+
@hover="mention2.setActiveIndex($event)"
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
<template #controls>
|
|
114
|
+
<div class="text-xs text-fg-2 p-2">
|
|
115
|
+
<div>value: {{ text2 || '(empty)' }}</div>
|
|
116
|
+
<div>trigger: {{ mention2.activeTrigger.value?.key ?? 'none' }}</div>
|
|
117
|
+
</div>
|
|
118
|
+
</template>
|
|
119
|
+
</Variant>
|
|
120
|
+
|
|
121
|
+
<Variant title="Action commands (/ strips text)">
|
|
122
|
+
<div class="w-[400px]">
|
|
123
|
+
<textarea
|
|
124
|
+
v-model="text3"
|
|
125
|
+
class="w-full h-24 px-2 py-1 bg-0 border border-line rounded-sm font-mono text-base text-fg-0 outline-none focus:border-accent resize-none"
|
|
126
|
+
placeholder="type @ to mention, / to run a command..."
|
|
127
|
+
v-on="mention3.handlers"
|
|
128
|
+
/>
|
|
129
|
+
<Mention
|
|
130
|
+
:open="mention3.open.value"
|
|
131
|
+
:items="mention3.filtered.value"
|
|
132
|
+
:active-index="mention3.activeIndex.value"
|
|
133
|
+
:loading="mention3.loading.value"
|
|
134
|
+
:anchor="mention3.virtualAnchor.value"
|
|
135
|
+
@select="mention3.confirm($event)"
|
|
136
|
+
@hover="mention3.setActiveIndex($event)"
|
|
137
|
+
/>
|
|
138
|
+
</div>
|
|
139
|
+
<template #controls>
|
|
140
|
+
<div class="text-xs text-fg-2 p-2">
|
|
141
|
+
<div>value: {{ text3 || '(empty)' }}</div>
|
|
142
|
+
<div>last action: {{ lastAction ?? 'none' }}</div>
|
|
143
|
+
</div>
|
|
144
|
+
</template>
|
|
145
|
+
</Variant>
|
|
146
|
+
|
|
147
|
+
<Variant title="Input at bottom (flip test)">
|
|
148
|
+
<div class="w-[400px] flex flex-col justify-end" style="height: calc(100vh - 100px)">
|
|
149
|
+
<textarea
|
|
150
|
+
class="w-full h-24 px-2 py-1 bg-0 border border-line rounded-sm font-mono text-base text-fg-0 outline-none focus:border-accent resize-none"
|
|
151
|
+
placeholder="type @ here to see panel flip upward..."
|
|
152
|
+
v-on="mention1.handlers"
|
|
153
|
+
/>
|
|
154
|
+
<Mention
|
|
155
|
+
:open="mention1.open.value"
|
|
156
|
+
:items="mention1.filtered.value"
|
|
157
|
+
:active-index="mention1.activeIndex.value"
|
|
158
|
+
:loading="mention1.loading.value"
|
|
159
|
+
:anchor="mention1.virtualAnchor.value"
|
|
160
|
+
@select="mention1.confirm($event)"
|
|
161
|
+
@hover="mention1.setActiveIndex($event)"
|
|
162
|
+
/>
|
|
163
|
+
</div>
|
|
164
|
+
</Variant>
|
|
165
|
+
</Story>
|
|
166
|
+
</template>
|