debugbar 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/debugbar/assets_controller.rb +1 -1
- data/build.sh +7 -0
- data/debugbar.gemspec +6 -1
- data/lib/debugbar/engine.rb +0 -8
- data/lib/debugbar/version.rb +1 -1
- data/lib/debugbar.rb +0 -7
- metadata +2 -54
- data/client/.gitignore +0 -25
- data/client/README.md +0 -0
- data/client/dist/.vite/manifest.json +0 -14
- data/client/dist/assets/debugbar-u5mP-5-z.js +0 -34
- data/client/dist/assets/ruby-logo-kn_8RniZ.svg +0 -946
- data/client/index.html +0 -78
- data/client/package-lock.json +0 -2393
- data/client/package.json +0 -32
- data/client/postcss.config.js +0 -6
- data/client/src/App.vue +0 -17
- data/client/src/AppDemo.vue +0 -29
- data/client/src/AppDev.vue +0 -20
- data/client/src/Debugbar.vue +0 -276
- data/client/src/assets/rails-logo.svg +0 -1
- data/client/src/assets/ruby-logo.svg +0 -946
- data/client/src/components/TabButton.vue +0 -32
- data/client/src/components/panels/CachePanel.vue +0 -41
- data/client/src/components/panels/JobsPanel.vue +0 -52
- data/client/src/components/panels/JsonPanel.vue +0 -15
- data/client/src/components/panels/LogsPanel.vue +0 -43
- data/client/src/components/panels/MessagesPanel.vue +0 -25
- data/client/src/components/panels/ModelsPanel.vue +0 -37
- data/client/src/components/panels/Panel.vue +0 -7
- data/client/src/components/panels/RequestPanel.vue +0 -98
- data/client/src/components/queries/QueriesPanel.vue +0 -17
- data/client/src/components/queries/QueryItem.vue +0 -65
- data/client/src/components/ui/Foldable.vue +0 -39
- data/client/src/components/ui/KeyValueTable.vue +0 -16
- data/client/src/components/ui/Row.vue +0 -14
- data/client/src/components/ui/logo-ruby.vue +0 -547
- data/client/src/demo.ts +0 -17
- data/client/src/dev.ts +0 -20
- data/client/src/main.ts +0 -17
- data/client/src/models/Config.ts +0 -27
- data/client/src/models/Request.ts +0 -183
- data/client/src/stores/RequestsStore.ts +0 -36
- data/client/src/stores/configStore.ts +0 -8
- data/client/src/style.css +0 -23
- data/client/src/types.d.ts +0 -9
- data/client/src/vite-env.d.ts +0 -1
- data/client/tailwind.config.js +0 -16
- data/client/tsconfig.json +0 -29
- data/client/tsconfig.node.json +0 -10
- data/client/vite.config.ts +0 -44
- data/fixtures/requests/1706607114--demo_post_list.json +0 -499
- data/fixtures/requests/1706607120--api_jobs.json +0 -176
- data/fixtures/requests/1706607123--api_jobs.json +0 -119
- data/fixtures/requests/1706607133--demo_slow_page.json +0 -130
- data/fixtures/requests/1706607136--demo_post.json +0 -164
- data/fixtures/requests/1706607136--demo_random_post.json +0 -106
- data/fixtures/requests/1706607141--api_errors.json +0 -73
- data/package-lock.json +0 -50
- data/package.json +0 -5
@@ -1,32 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
const props = defineProps<{
|
3
|
-
label: string
|
4
|
-
count?: number
|
5
|
-
isActive: boolean
|
6
|
-
}>()
|
7
|
-
</script>
|
8
|
-
|
9
|
-
<template>
|
10
|
-
<button
|
11
|
-
class="text-sm flex items-center space-x-1 px-3 py-2 border-0"
|
12
|
-
:class="{
|
13
|
-
'bg-stone-300 rounded-sm': props.isActive,
|
14
|
-
}"
|
15
|
-
>
|
16
|
-
<span :class="{ 'font-medium': props.isActive }"><slot /></span>
|
17
|
-
<span
|
18
|
-
v-if="props.count != undefined"
|
19
|
-
class="p-0.5 rounded-full text-xs"
|
20
|
-
:class="{
|
21
|
-
'px-1.5': props.count < 10,
|
22
|
-
hidden: props.count == 0,
|
23
|
-
'bg-stone-300': props.count > 0 && !props.isActive,
|
24
|
-
'bg-stone-400': props.count > 0 && props.isActive,
|
25
|
-
// '!bg-red-700 !text-white': props.count > 10, // TODO: Should be PER TAB (30 models is fine but 30 queries is a lot)
|
26
|
-
}"
|
27
|
-
v-text="props.count"
|
28
|
-
/>
|
29
|
-
</button>
|
30
|
-
</template>
|
31
|
-
|
32
|
-
<style scoped></style>
|
@@ -1,41 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
import type { Cache } from "@/models/Request.ts"
|
3
|
-
import Panel from "@/components/panels/Panel.vue"
|
4
|
-
|
5
|
-
const props = defineProps<{
|
6
|
-
cache: Cache[]
|
7
|
-
}>()
|
8
|
-
</script>
|
9
|
-
|
10
|
-
<template>
|
11
|
-
<panel>
|
12
|
-
<div v-if="props.cache.length == 0">
|
13
|
-
<div class="text-gray-500">No cache used.</div>
|
14
|
-
</div>
|
15
|
-
|
16
|
-
<div class="space-y-3">
|
17
|
-
<div v-for="cache in props.cache" class="flex items-center space-x-8">
|
18
|
-
<div class="w-24 text-sm text-right text-gray-400">{{ cache.time }}</div>
|
19
|
-
<div class="w-16 text-right">
|
20
|
-
<span
|
21
|
-
class="px-1 py-0.5 rounded text-white text-xs font-mono font-medium bg-stone-400"
|
22
|
-
:class="{
|
23
|
-
'!bg-emerald-500': cache.label == 'hit',
|
24
|
-
'!bg-indigo-500': cache.label == 'write',
|
25
|
-
'!bg-amber-400': cache.label == 'read',
|
26
|
-
'!bg-red-400': cache.label == 'delete',
|
27
|
-
}"
|
28
|
-
>
|
29
|
-
{{ cache.label }}
|
30
|
-
</span>
|
31
|
-
</div>
|
32
|
-
<div class="text-gray-800">
|
33
|
-
<div class="font-medium" title="cache key">{{ cache.key }}</div>
|
34
|
-
<div class="text-xs text-gray-400" title="transaction_id">{{ cache.transaction_id }}</div>
|
35
|
-
</div>
|
36
|
-
</div>
|
37
|
-
</div>
|
38
|
-
</panel>
|
39
|
-
</template>
|
40
|
-
|
41
|
-
<style scoped></style>
|
@@ -1,52 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
import { Job } from "@/models/Request.ts"
|
3
|
-
import Panel from "@/components/panels/Panel.vue"
|
4
|
-
|
5
|
-
const props = defineProps<{
|
6
|
-
jobs: Job[]
|
7
|
-
}>()
|
8
|
-
|
9
|
-
function formatTs(ts: number) {
|
10
|
-
if (ts == null) {
|
11
|
-
return "-"
|
12
|
-
}
|
13
|
-
return new Date(ts * 1000).toLocaleString()
|
14
|
-
}
|
15
|
-
</script>
|
16
|
-
|
17
|
-
<template>
|
18
|
-
<panel>
|
19
|
-
<div v-if="props.jobs.length == 0">
|
20
|
-
<div class="text-gray-500">No jobs enqueued.</div>
|
21
|
-
</div>
|
22
|
-
|
23
|
-
<table v-if="props.jobs.length > 0" class="my-4 mx-6 divide-y divide-stone-300">
|
24
|
-
<thead>
|
25
|
-
<tr>
|
26
|
-
<th scope="col" class="w-36 py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-stone-900 sm:pl-0">Job</th>
|
27
|
-
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-stone-900">Args</th>
|
28
|
-
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-stone-900">Logs</th>
|
29
|
-
</tr>
|
30
|
-
</thead>
|
31
|
-
<tbody class="divide-y divide-stone-200">
|
32
|
-
<tr v-for="(v, _k) in props.jobs" :key="v.id">
|
33
|
-
<td class="whitespace-nowrap p-4 pr-8 text-stone-900">
|
34
|
-
<div class="text-lg font-bold" v-text="v.class"></div>
|
35
|
-
<div class="text-stone-600 text-sm">
|
36
|
-
<div v-text="'Queue: ' + v.queue"></div>
|
37
|
-
<div v-text="'At: ' + formatTs(v.scheduled_at)"></div>
|
38
|
-
</div>
|
39
|
-
</td>
|
40
|
-
<td class="whitespace-nowrap px-3 p-4 pr-8 text-sm">
|
41
|
-
<highlightjs language="json" :code="JSON.stringify(v.args, null, 2)" />
|
42
|
-
</td>
|
43
|
-
<td class="whitespace-nowrap px-3 p-4 pr-8 text-sm text-stone-500">
|
44
|
-
<div v-for="(log, k) in v.logs" v-html="(k > 0 ? ' '.repeat(k) + '↳ ' : '') + log" class="" />
|
45
|
-
</td>
|
46
|
-
</tr>
|
47
|
-
</tbody>
|
48
|
-
</table>
|
49
|
-
</panel>
|
50
|
-
</template>
|
51
|
-
|
52
|
-
<style scoped></style>
|
@@ -1,15 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
import type { BackendRequest } from "@/models/Request.ts"
|
3
|
-
|
4
|
-
const props = defineProps<{
|
5
|
-
currentRequest: BackendRequest
|
6
|
-
}>()
|
7
|
-
</script>
|
8
|
-
|
9
|
-
<template>
|
10
|
-
<div class="p-4 leading-8">
|
11
|
-
<highlightjs language="json" :code="JSON.stringify(props.currentRequest, null, 2)" />
|
12
|
-
</div>
|
13
|
-
</template>
|
14
|
-
|
15
|
-
<style scoped></style>
|
@@ -1,43 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
import { Log } from "@/models/Request.ts"
|
3
|
-
import Panel from "@/components/panels/Panel.vue"
|
4
|
-
|
5
|
-
const props = defineProps<{
|
6
|
-
logs: Log[]
|
7
|
-
}>()
|
8
|
-
|
9
|
-
function message(log: Log): string {
|
10
|
-
const str = log.progname || log.message || ""
|
11
|
-
return str.replace(" ", " ")
|
12
|
-
}
|
13
|
-
</script>
|
14
|
-
|
15
|
-
<template>
|
16
|
-
<panel>
|
17
|
-
<div v-if="props.logs.length == 0">
|
18
|
-
<div class="text-gray-500">No logs to show. Are you using the correct minimum level in your config?</div>
|
19
|
-
</div>
|
20
|
-
|
21
|
-
<div v-for="log in props.logs" class="flex items-center space-y-1 space-x-3">
|
22
|
-
<div class="w-32 text-right text-gray-400">{{ log.time }}</div>
|
23
|
-
<div class="w-20 text-center">
|
24
|
-
<span
|
25
|
-
class="px-1 py-0.5 rounded text-white text-xs font-mono font-medium"
|
26
|
-
:class="{
|
27
|
-
'bg-stone-400': log.severity == 0,
|
28
|
-
'bg-blue-500': log.severity == 1,
|
29
|
-
'bg-amber-400': log.severity == 2,
|
30
|
-
'bg-red-400': log.severity == 2,
|
31
|
-
'bg-fuchsia-500': log.severity >= 3,
|
32
|
-
}"
|
33
|
-
:title="log.severity_label"
|
34
|
-
>
|
35
|
-
{{ log.severity_label }}
|
36
|
-
</span>
|
37
|
-
</div>
|
38
|
-
<div class="text-gray-800" v-html="message(log)"></div>
|
39
|
-
</div>
|
40
|
-
</panel>
|
41
|
-
</template>
|
42
|
-
|
43
|
-
<style scoped></style>
|
@@ -1,25 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
import { Message } from "@/models/Request.ts"
|
3
|
-
import Panel from "@/components/panels/Panel.vue"
|
4
|
-
|
5
|
-
const props = defineProps<{
|
6
|
-
messages: Message[]
|
7
|
-
}>()
|
8
|
-
</script>
|
9
|
-
|
10
|
-
<template>
|
11
|
-
<panel>
|
12
|
-
<div class="flex flex-col space-y-8">
|
13
|
-
<!-- <div v-for="(msg, idx) in props.messages" v-text="msg.msg"></div>-->
|
14
|
-
<div v-for="(msg, _idx) in props.messages" class="space-y-3">
|
15
|
-
<div class="font-bold text-lg" v-text="msg.msg"></div>
|
16
|
-
<div class="ml-4">
|
17
|
-
<div v-if="!msg.extra">–</div>
|
18
|
-
<highlightjs v-if="msg.extra" language="json" :code="JSON.stringify(msg.extra)" />
|
19
|
-
</div>
|
20
|
-
</div>
|
21
|
-
</div>
|
22
|
-
</panel>
|
23
|
-
</template>
|
24
|
-
|
25
|
-
<style scoped></style>
|
@@ -1,37 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
import Panel from "@/components/panels/Panel.vue"
|
3
|
-
|
4
|
-
const props = defineProps<{
|
5
|
-
models: { [key: string]: number }
|
6
|
-
count: number
|
7
|
-
}>()
|
8
|
-
</script>
|
9
|
-
|
10
|
-
<template>
|
11
|
-
<panel>
|
12
|
-
<div v-if="props.count == 0">
|
13
|
-
<div class="text-gray-500">No models were initialized.</div>
|
14
|
-
</div>
|
15
|
-
|
16
|
-
<table v-if="props.count > 0" class="divide-y divide-stone-300">
|
17
|
-
<thead>
|
18
|
-
<tr>
|
19
|
-
<th scope="col" class="w-36 py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-stone-900 sm:pl-0">
|
20
|
-
Model
|
21
|
-
</th>
|
22
|
-
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-stone-900">Count</th>
|
23
|
-
</tr>
|
24
|
-
</thead>
|
25
|
-
<tbody class="divide-y divide-stone-200">
|
26
|
-
<tr v-for="(v, k) in props.models" :key="k">
|
27
|
-
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-stone-900 sm:pl-0">
|
28
|
-
{{ k }}
|
29
|
-
</td>
|
30
|
-
<td class="whitespace-nowrap px-3 py-4 text-sm text-stone-500 text-center">{{ v }}</td>
|
31
|
-
</tr>
|
32
|
-
</tbody>
|
33
|
-
</table>
|
34
|
-
</panel>
|
35
|
-
</template>
|
36
|
-
|
37
|
-
<style scoped></style>
|
@@ -1,98 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
import omit from "lodash/omit"
|
3
|
-
import Panel from "@/components/panels/Panel.vue"
|
4
|
-
import { BackendRequest } from "@/models/Request.ts"
|
5
|
-
import KeyValueTable from "@/components/ui/KeyValueTable.vue"
|
6
|
-
import Row from "@/components/ui/Row.vue"
|
7
|
-
import Foldable from "@/components/ui/Foldable.vue"
|
8
|
-
|
9
|
-
defineProps<{
|
10
|
-
request: BackendRequest
|
11
|
-
}>()
|
12
|
-
</script>
|
13
|
-
|
14
|
-
<template>
|
15
|
-
<div class="flex">
|
16
|
-
<div class="w-1/2">
|
17
|
-
<panel>
|
18
|
-
<h2 class="mt-0.5 mb-2 px-2 py-1 bg-stone-300 text-black tracking-wide text-xs uppercase font-bold rounded">
|
19
|
-
HTTP Request
|
20
|
-
</h2>
|
21
|
-
|
22
|
-
<key-value-table>
|
23
|
-
<row label="Method">{{ request.meta.method }}</row>
|
24
|
-
<row label="URL">{{ request.meta.path }}</row>
|
25
|
-
<row label="Params">
|
26
|
-
<highlightjs
|
27
|
-
class="text-sm"
|
28
|
-
language="json"
|
29
|
-
:code="JSON.stringify(omit(request.meta.params, ['controller', 'action']), null, 2)"
|
30
|
-
/>
|
31
|
-
</row>
|
32
|
-
<row label="Header: Version">
|
33
|
-
{{ request.request.headers["Version"] }}
|
34
|
-
</row>
|
35
|
-
<row label="Header: Cache-Control">
|
36
|
-
{{ request.request.headers["Cache-Control"] }}
|
37
|
-
</row>
|
38
|
-
</key-value-table>
|
39
|
-
|
40
|
-
<foldable class="py-4" label="All Headers">
|
41
|
-
<key-value-table>
|
42
|
-
<row v-for="(v, k) in request.request.headers" :key="k" :label="k as unknown as string">{{ v }}</row>
|
43
|
-
</key-value-table>
|
44
|
-
</foldable>
|
45
|
-
|
46
|
-
<div class="py-3 text-right italic text-sm text-stone-500">
|
47
|
-
What else would like to see here?
|
48
|
-
<a target="_blank" class="underline font-bold" href="https://github.com/julienbourdeau/debugbar/discussions"
|
49
|
-
>Tell me!</a
|
50
|
-
>
|
51
|
-
</div>
|
52
|
-
</panel>
|
53
|
-
</div>
|
54
|
-
|
55
|
-
<div class="w-1/2">
|
56
|
-
<panel>
|
57
|
-
<h2 class="mt-0.5 mb-2 px-2 py-1 bg-stone-300 text-black tracking-wide text-xs uppercase font-bold rounded">
|
58
|
-
Routing
|
59
|
-
</h2>
|
60
|
-
|
61
|
-
<key-value-table>
|
62
|
-
<row title="Controller"> {{ request.meta.controller }} > {{ request.meta.action }} </row>
|
63
|
-
</key-value-table>
|
64
|
-
</panel>
|
65
|
-
|
66
|
-
<panel>
|
67
|
-
<h2 class="mt-0.5 mb-2 px-2 py-1 bg-stone-300 text-black tracking-wide text-xs uppercase font-bold rounded">
|
68
|
-
HTTP Response
|
69
|
-
</h2>
|
70
|
-
|
71
|
-
<div v-if="!request.response?.status">
|
72
|
-
<div class="py-3 text-sm text-stone-500">
|
73
|
-
The response was not captured.
|
74
|
-
<a target="_blank" class="underline font-bold" href="https://debugbar.dev/docs/known-limitations/"
|
75
|
-
>Learn more</a
|
76
|
-
>
|
77
|
-
</div>
|
78
|
-
</div>
|
79
|
-
|
80
|
-
<div v-if="request.response?.status">
|
81
|
-
<key-value-table>
|
82
|
-
<row label="Status">{{ request.response.status }}</row>
|
83
|
-
<row label="Body">{{ request.response.body.substring(0, 140) }}</row>
|
84
|
-
<row label="Header: Content-Type">
|
85
|
-
{{ request.response.headers["Content-Type"] }}
|
86
|
-
</row>
|
87
|
-
</key-value-table>
|
88
|
-
|
89
|
-
<foldable class="py-4" label="All Headers">
|
90
|
-
<key-value-table>
|
91
|
-
<row v-for="(v, k) in request.response.headers" :key="k" :label="k as unknown as string">{{ v }}</row>
|
92
|
-
</key-value-table>
|
93
|
-
</foldable>
|
94
|
-
</div>
|
95
|
-
</panel>
|
96
|
-
</div>
|
97
|
-
</div>
|
98
|
-
</template>
|
@@ -1,17 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
import type { BackendRequest } from "@/models/Request.ts"
|
3
|
-
import QueryItem from "@/components/queries/QueryItem.vue"
|
4
|
-
import Panel from "@/components/panels/Panel.vue"
|
5
|
-
|
6
|
-
const props = defineProps<{
|
7
|
-
currentRequest: BackendRequest
|
8
|
-
}>()
|
9
|
-
</script>
|
10
|
-
|
11
|
-
<template>
|
12
|
-
<panel>
|
13
|
-
<div class="flex flex-col space-y-8">
|
14
|
-
<query-item v-for="query in props.currentRequest.queries" :key="query.id" :query="query" />
|
15
|
-
</div>
|
16
|
-
</panel>
|
17
|
-
</template>
|
@@ -1,65 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
import { format } from "sql-formatter"
|
3
|
-
import { ChevronDownIcon } from "@heroicons/vue/16/solid"
|
4
|
-
|
5
|
-
import { Query } from "@/models/Request.ts"
|
6
|
-
import { reactive } from "vue"
|
7
|
-
|
8
|
-
const props = defineProps<{
|
9
|
-
query: Query
|
10
|
-
}>()
|
11
|
-
|
12
|
-
const state = reactive({
|
13
|
-
isOpen: true,
|
14
|
-
isFormatted: false,
|
15
|
-
})
|
16
|
-
|
17
|
-
function copyToClipboard(text: string) {
|
18
|
-
const type = "text/plain"
|
19
|
-
const blob = new Blob([text], { type })
|
20
|
-
const data = [new ClipboardItem({ [type]: blob })]
|
21
|
-
navigator.clipboard.write(data)
|
22
|
-
}
|
23
|
-
</script>
|
24
|
-
|
25
|
-
<template>
|
26
|
-
<div>
|
27
|
-
<div class="flex items-center space-x-3">
|
28
|
-
<button class="flex items-center space-x-1" @click="state.isOpen = !state.isOpen">
|
29
|
-
<chevron-down-icon
|
30
|
-
class="size-4"
|
31
|
-
:class="{
|
32
|
-
'-rotate-90': !state.isOpen,
|
33
|
-
}"
|
34
|
-
/>
|
35
|
-
<span class="font-bold text-lg">{{ query.name }}</span>
|
36
|
-
</button>
|
37
|
-
<span v-if="props.query.cached" class="px-1 py-0.5 rounded text-xs bg-sky-600 text-white">cached</span>
|
38
|
-
<span v-if="props.query.async" class="px-1 py-0.5 rounded text-xs bg-emerald-600 text-white">async</span>
|
39
|
-
<div v-if="state.isOpen">
|
40
|
-
<span
|
41
|
-
@click="state.isFormatted = !state.isFormatted"
|
42
|
-
class="px-3 text-xs uppercase text-stone-400 cursor-pointer"
|
43
|
-
title="Format SQL query"
|
44
|
-
v-text="state.isFormatted ? 'unformat' : 'format'"
|
45
|
-
/>
|
46
|
-
<span
|
47
|
-
@click="copyToClipboard(query.sql)"
|
48
|
-
class="px-3 text-xs uppercase text-stone-400 cursor-pointer"
|
49
|
-
title="Copy SQL query to clipboard"
|
50
|
-
>copy</span
|
51
|
-
>
|
52
|
-
</div>
|
53
|
-
</div>
|
54
|
-
|
55
|
-
<div v-if="state.isOpen" class="mt-4 ml-4">
|
56
|
-
<div class="">
|
57
|
-
<highlightjs language="sql" :code="state.isFormatted ? format(query.sql) : query.sql" />
|
58
|
-
</div>
|
59
|
-
<div class="mt-3 text-stone-400 text-sm">
|
60
|
-
<div v-text="query.source[0]"></div>
|
61
|
-
<div v-if="query.source.length > 1" v-for="s in query.source.slice(1)" class="pl-4" v-text="'↳ ' + s"></div>
|
62
|
-
</div>
|
63
|
-
</div>
|
64
|
-
</div>
|
65
|
-
</template>
|
@@ -1,39 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
import { reactive } from "vue"
|
3
|
-
import { ChevronDownIcon } from "@heroicons/vue/16/solid"
|
4
|
-
|
5
|
-
const props = withDefaults(
|
6
|
-
defineProps<{
|
7
|
-
label: string
|
8
|
-
isOpen?: boolean
|
9
|
-
}>(),
|
10
|
-
{
|
11
|
-
isOpen: false,
|
12
|
-
}
|
13
|
-
)
|
14
|
-
|
15
|
-
const state = reactive({
|
16
|
-
isOpen: props.isOpen,
|
17
|
-
})
|
18
|
-
</script>
|
19
|
-
|
20
|
-
<template>
|
21
|
-
<div>
|
22
|
-
<div>
|
23
|
-
<button class="flex items-center space-x-1" @click="state.isOpen = !state.isOpen">
|
24
|
-
<chevron-down-icon
|
25
|
-
class="size-4"
|
26
|
-
:class="{
|
27
|
-
'-rotate-90': !state.isOpen,
|
28
|
-
}"
|
29
|
-
/>
|
30
|
-
<span class="font-medium">{{ props.label }}</span>
|
31
|
-
</button>
|
32
|
-
</div>
|
33
|
-
|
34
|
-
<div v-if="state.isOpen">
|
35
|
-
<slot />
|
36
|
-
</div>
|
37
|
-
</div>
|
38
|
-
</template>
|
39
|
-
<style scoped></style>
|
@@ -1,16 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
const props = defineProps<{
|
3
|
-
keyLabel?: string
|
4
|
-
valueLabel?: string
|
5
|
-
}>()
|
6
|
-
</script>
|
7
|
-
|
8
|
-
<template>
|
9
|
-
<table class="break-all w-full border-separate border-spacing-1">
|
10
|
-
<tr v-if="props.keyLabel || props.valueLabel">
|
11
|
-
<th v-text="props.keyLabel" class="bg-amber-50 w-40 px-3 py-1"></th>
|
12
|
-
<th v-text="props.valueLabel" class="bg-amber-50 px-3 py-1"></th>
|
13
|
-
</tr>
|
14
|
-
<slot />
|
15
|
-
</table>
|
16
|
-
</template>
|
@@ -1,14 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
const props = defineProps<{
|
3
|
-
label?: string
|
4
|
-
}>()
|
5
|
-
</script>
|
6
|
-
|
7
|
-
<template>
|
8
|
-
<tr>
|
9
|
-
<td v-text="props.label" class="w-40 font-medium bg-stone-50 px-3 py-1"></td>
|
10
|
-
<td class="px-3 py-1"><slot /></td>
|
11
|
-
</tr>
|
12
|
-
</template>
|
13
|
-
|
14
|
-
<style scoped></style>
|