@greendrake/ui 0.6.0 → 0.8.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/README.md +53 -11
- package/package.json +5 -5
- package/src/BottomNav.vue +2 -2
- package/src/CircularProgress.vue +6 -2
- package/src/ContextMenu.vue +77 -0
- package/src/Copy.vue +2 -1
- package/src/FieldTip.ts +4 -4
- package/src/InfoBlock.vue +45 -0
- package/src/InfoTip.vue +33 -0
- package/src/MenuButton.vue +3 -1
- package/src/ModalOnClick.vue +13 -6
- package/src/RowSelect.ts +4 -27
- package/src/SplitPane.vue +85 -0
- package/src/SwitchRow.vue +74 -0
- package/src/TabBar.vue +11 -0
- package/src/TabPanels.vue +38 -0
- package/src/{FieldTip.scss → Tip.scss} +7 -7
- package/src/TreeView.vue +368 -69
- package/src/capturePointer.ts +15 -0
- package/src/main.ts +12 -3
- package/src/messages.ts +17 -7
- package/src/useSelection.ts +67 -72
- package/src/useWidthDrag.ts +38 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// An on/off switch shaped as a row: the label at the leading edge, a track and
|
|
3
|
+
// knob trailing, showing the current state rather than leaving an ambiguous
|
|
4
|
+
// label to imply it.
|
|
5
|
+
//
|
|
6
|
+
// An <a role="switch"> rather than a checkbox: in a menu the row sits among
|
|
7
|
+
// menu items and has to read and be styled as one of them (the host classes it
|
|
8
|
+
// `menu-item`), while the role keeps its on/off state honest to assistive tech.
|
|
9
|
+
// Layout comes from the host's class, which side the label sits on included —
|
|
10
|
+
// everything here is the switch itself.
|
|
11
|
+
const on = defineModel<boolean>({ required: true })
|
|
12
|
+
defineProps<{
|
|
13
|
+
label: string
|
|
14
|
+
// Tooltip, where what the switch DOES is worth naming apart from what it
|
|
15
|
+
// is: the colour-mode row is labelled "Dark mode" but offers "Light mode"
|
|
16
|
+
// while dark. Defaults to the label.
|
|
17
|
+
title?: string
|
|
18
|
+
}>()
|
|
19
|
+
</script>
|
|
20
|
+
<template>
|
|
21
|
+
<!-- .stop, because MenuButton's popper dismisses itself on any click within
|
|
22
|
+
it: right for an item that IS a choice, wrong for a control that acts in
|
|
23
|
+
place. Without it the menu vanishes on the very frame the knob would
|
|
24
|
+
have moved, and the setting can only be read back by reopening it. -->
|
|
25
|
+
<a href="#" class="SwitchRow" role="switch" :aria-checked="on" :title="title ?? label" @click.prevent.stop="on = !on">
|
|
26
|
+
<span class="SwitchRow__text">{{ label }}</span>
|
|
27
|
+
<span class="SwitchRow__toggle" :class="{ 'SwitchRow__toggle--on': on }"><span class="SwitchRow__knob"></span></span>
|
|
28
|
+
</a>
|
|
29
|
+
</template>
|
|
30
|
+
<style lang="scss">
|
|
31
|
+
.SwitchRow {
|
|
32
|
+
// The row is an <a> (for role="switch"), so the global `a, a span` rule would
|
|
33
|
+
// paint it violet — but it's a toggle, not a link. Pin the label span too
|
|
34
|
+
// (the `a span` rule explicitly colours it, so the anchor colour alone won't
|
|
35
|
+
// reach it).
|
|
36
|
+
color: var(--font-color);
|
|
37
|
+
text-decoration: none;
|
|
38
|
+
gap: var(--space-2);
|
|
39
|
+
&__text {
|
|
40
|
+
color: var(--font-color);
|
|
41
|
+
}
|
|
42
|
+
&__toggle {
|
|
43
|
+
flex: 0 0 auto;
|
|
44
|
+
width: 40px;
|
|
45
|
+
height: 22px;
|
|
46
|
+
border-radius: var(--border-radius-pill);
|
|
47
|
+
background: var(--border-color);
|
|
48
|
+
transition: background 0.15s;
|
|
49
|
+
position: relative;
|
|
50
|
+
}
|
|
51
|
+
&__knob {
|
|
52
|
+
position: absolute;
|
|
53
|
+
top: 2px;
|
|
54
|
+
inset-inline-start: 2px;
|
|
55
|
+
width: 18px;
|
|
56
|
+
height: 18px;
|
|
57
|
+
border-radius: 50%;
|
|
58
|
+
background: #fff;
|
|
59
|
+
transition: transform 0.15s;
|
|
60
|
+
}
|
|
61
|
+
&__toggle--on {
|
|
62
|
+
background: var(--accent-color);
|
|
63
|
+
.SwitchRow__knob {
|
|
64
|
+
// The knob travels toward the inline end, which is leftwards under
|
|
65
|
+
// an RTL layout — transforms have no logical form, so the direction
|
|
66
|
+
// is stated rather than derived.
|
|
67
|
+
transform: translateX(18px);
|
|
68
|
+
}
|
|
69
|
+
[dir='rtl'] & .SwitchRow__knob {
|
|
70
|
+
transform: translateX(-18px);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
</style>
|
package/src/TabBar.vue
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Component } from 'vue'
|
|
3
|
+
|
|
2
4
|
export interface TabDef {
|
|
3
5
|
id: string
|
|
4
6
|
label: string
|
|
5
7
|
closable?: boolean
|
|
6
8
|
}
|
|
9
|
+
|
|
10
|
+
// A component and the props its host binds it with.
|
|
11
|
+
export interface Pane {
|
|
12
|
+
component: Component
|
|
13
|
+
props?: Record<string, unknown>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// A tab and the pane it shows.
|
|
17
|
+
export interface TabPane extends TabDef, Pane {}
|
|
7
18
|
</script>
|
|
8
19
|
<script setup lang="ts">
|
|
9
20
|
import { computed } from 'vue'
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { TabPane } from './TabBar.vue'
|
|
3
|
+
|
|
4
|
+
/** A tab: the tab strip's entry plus the panel it shows. */
|
|
5
|
+
export interface TabPanel extends TabPane {
|
|
6
|
+
// The panel outlives being switched away from: kept as it was, not mounted again
|
|
7
|
+
// on coming back. Unset, it is unmounted, and whatever it holds open closes.
|
|
8
|
+
keepAlive?: boolean
|
|
9
|
+
}
|
|
10
|
+
</script>
|
|
11
|
+
<script setup lang="ts">
|
|
12
|
+
// A tab strip over one panel at a time, the tabs declared as data. The strip and the
|
|
13
|
+
// panel are the host's children, laid out as it lays out its own.
|
|
14
|
+
import { computed } from 'vue'
|
|
15
|
+
import TabBar from './TabBar.vue'
|
|
16
|
+
|
|
17
|
+
const props = defineProps<{ tabs: TabPanel[] }>()
|
|
18
|
+
|
|
19
|
+
// The open tab's id: bound by a host that opens tabs itself, kept here otherwise.
|
|
20
|
+
const active = defineModel<string>('active')
|
|
21
|
+
|
|
22
|
+
// Only ever a registry id, so the lookup always resolves.
|
|
23
|
+
const activeTab = computed({
|
|
24
|
+
get: () => active.value ?? props.tabs[0]!.id,
|
|
25
|
+
set: id => {
|
|
26
|
+
active.value = id
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
const panel = computed(() => props.tabs.find(t => t.id === activeTab.value)!)
|
|
30
|
+
</script>
|
|
31
|
+
<template>
|
|
32
|
+
<TabBar v-model="activeTab" :tabs="tabs" />
|
|
33
|
+
<!-- A kept panel is deactivated rather than unmounted while another shows. -->
|
|
34
|
+
<KeepAlive>
|
|
35
|
+
<component :is="panel.component" v-if="panel.keepAlive" :key="panel.id" v-bind="panel.props" />
|
|
36
|
+
</KeepAlive>
|
|
37
|
+
<component :is="panel.component" v-if="!panel.keepAlive" v-bind="panel.props" />
|
|
38
|
+
</template>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// Styling for the
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
.tippy-box[data-theme~='
|
|
1
|
+
// Styling for the text tooltips FieldTip.ts and InfoTip.vue show, scoped to the `tip`
|
|
2
|
+
// theme they apply. Self-contained (there is no global tippy.js base stylesheet) so it
|
|
3
|
+
// styles only these tips and never the other themed, arrow-less tippies (menus,
|
|
4
|
+
// async-search): a filled box with a pointer arrow, its box/arrow geometry mirroring
|
|
5
|
+
// tippy.js' own tippy.css. The fill is the --tippy-background-color token, defaulting
|
|
6
|
+
// to violet.
|
|
7
|
+
.tippy-box[data-theme~='tip'] {
|
|
8
8
|
position: relative;
|
|
9
9
|
background-color: var(--tippy-background-color, #5335e8);
|
|
10
10
|
color: #fff;
|