@datagouv/components-next 1.0.2-dev.243 → 1.0.2-dev.244
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/dist/components-next.js +190 -142
- package/dist/components.css +1 -1
- package/package.json +1 -1
- package/src/components/SegmentedControl.vue +78 -0
- package/src/main.ts +4 -0
- package/src/types/organizations.ts +3 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="flex"
|
|
4
|
+
:class="underline ? 'border-b border-gray-default gap-4' : ['bg-gray-lower rounded p-1 gap-1', { 'w-fit': !grow }]"
|
|
5
|
+
role="group"
|
|
6
|
+
@keydown="onContainerKeydown"
|
|
7
|
+
>
|
|
8
|
+
<button
|
|
9
|
+
v-for="option in options"
|
|
10
|
+
:key="option.value"
|
|
11
|
+
type="button"
|
|
12
|
+
:aria-pressed="option.value === modelValue"
|
|
13
|
+
:tabindex="option.value === modelValue ? 0 : -1"
|
|
14
|
+
class="flex items-center justify-center gap-2 text-sm"
|
|
15
|
+
:class="[
|
|
16
|
+
underline ? 'py-2 px-1' : 'rounded py-1.5 px-3',
|
|
17
|
+
grow ? 'flex-1' : '',
|
|
18
|
+
option.value === modelValue
|
|
19
|
+
? underline
|
|
20
|
+
? 'text-new-primary font-bold border-b-2 border-b-new-primary -mb-px'
|
|
21
|
+
: 'bg-white font-bold shadow-sm'
|
|
22
|
+
: 'text-gray-medium hover:text-gray-title',
|
|
23
|
+
]"
|
|
24
|
+
@click="$emit('update:modelValue', option.value)"
|
|
25
|
+
>
|
|
26
|
+
<component
|
|
27
|
+
:is="option.icon"
|
|
28
|
+
v-if="option.icon"
|
|
29
|
+
class="size-4"
|
|
30
|
+
/>
|
|
31
|
+
{{ option.label }}
|
|
32
|
+
</button>
|
|
33
|
+
</div>
|
|
34
|
+
</template>
|
|
35
|
+
|
|
36
|
+
<script setup lang="ts">
|
|
37
|
+
import type { Component } from 'vue'
|
|
38
|
+
|
|
39
|
+
export type SegmentedControlOption = {
|
|
40
|
+
value: string
|
|
41
|
+
label: string
|
|
42
|
+
icon?: Component
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const props = withDefaults(defineProps<{
|
|
46
|
+
modelValue: string
|
|
47
|
+
options: Array<SegmentedControlOption>
|
|
48
|
+
// Fill the available width and stretch the segments (default: fit content)
|
|
49
|
+
grow?: boolean
|
|
50
|
+
// Underline variant: left-aligned tabs over a bottom border, active segment
|
|
51
|
+
// in blue with a thicker blue underline (default: pill track)
|
|
52
|
+
underline?: boolean
|
|
53
|
+
}>(), {
|
|
54
|
+
grow: false,
|
|
55
|
+
underline: false,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const emit = defineEmits<{
|
|
59
|
+
'update:modelValue': [value: string]
|
|
60
|
+
}>()
|
|
61
|
+
|
|
62
|
+
// Roving tabindex: only the active segment is in the tab order; arrow keys
|
|
63
|
+
// move between segments (standard segmented-control keyboard model).
|
|
64
|
+
function onContainerKeydown(event: KeyboardEvent) {
|
|
65
|
+
const direction = event.key === 'ArrowRight' ? 1 : event.key === 'ArrowLeft' ? -1 : 0
|
|
66
|
+
if (!direction && event.key !== 'Home' && event.key !== 'End') return
|
|
67
|
+
event.preventDefault()
|
|
68
|
+
const index = props.options.findIndex(o => o.value === props.modelValue)
|
|
69
|
+
const last = props.options.length - 1
|
|
70
|
+
const next = event.key === 'Home'
|
|
71
|
+
? 0
|
|
72
|
+
: event.key === 'End'
|
|
73
|
+
? last
|
|
74
|
+
: Math.min(last, Math.max(0, index + direction))
|
|
75
|
+
const option = props.options[next]
|
|
76
|
+
if (option) emit('update:modelValue', option.value)
|
|
77
|
+
}
|
|
78
|
+
</script>
|
package/src/main.ts
CHANGED
|
@@ -104,6 +104,7 @@ import Tooltip from './components/Tooltip.vue'
|
|
|
104
104
|
import Toggletip from './components/Toggletip.vue'
|
|
105
105
|
import TopicCard from './components/TopicCard.vue'
|
|
106
106
|
import TranslationT from './components/TranslationT.vue'
|
|
107
|
+
import SegmentedControl from './components/SegmentedControl.vue'
|
|
107
108
|
import GlobalSearch from './components/Search/GlobalSearch.vue'
|
|
108
109
|
import SearchInput from './components/Search/SearchInput.vue'
|
|
109
110
|
import SearchableSelect from './components/Form/SearchableSelect.vue'
|
|
@@ -120,6 +121,8 @@ import { configKey, useComponentsConfig, type PluginConfig } from './config.js'
|
|
|
120
121
|
import { ofetch } from 'ofetch'
|
|
121
122
|
import { useTranslation } from './composables/useTranslation'
|
|
122
123
|
|
|
124
|
+
export type { SegmentedControlOption } from './components/SegmentedControl.vue'
|
|
125
|
+
|
|
123
126
|
export { Toaster, toast } from 'vue-sonner'
|
|
124
127
|
|
|
125
128
|
export * from './composables/useActiveDescendant'
|
|
@@ -421,6 +424,7 @@ export {
|
|
|
421
424
|
TranslationT,
|
|
422
425
|
UserActivityList,
|
|
423
426
|
GlobalSearch,
|
|
427
|
+
SegmentedControl,
|
|
424
428
|
SearchInput,
|
|
425
429
|
SearchableSelect,
|
|
426
430
|
SelectGroup,
|
|
@@ -78,4 +78,7 @@ export type Organization = {
|
|
|
78
78
|
// means published. Like `presentation_blocs`, only present when requested with
|
|
79
79
|
// X-Fields.
|
|
80
80
|
presentation_blocs_published_at?: string | null
|
|
81
|
+
banner_color?: string | null
|
|
82
|
+
banner_image?: string | null
|
|
83
|
+
banner_image_position?: number
|
|
81
84
|
}
|