@nexxtmove/ui 1.25.0 → 1.27.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/dist/components/molecules/PageCard/PageCard.vue.d.ts +42 -0
- package/dist/components/molecules/PageMockup/PageMockup.vue.d.ts +9 -0
- package/dist/components/molecules/PageTypeTile/PageTypeTile.vue.d.ts +17 -0
- package/dist/components/molecules/TemplateCard/TemplateCard.vue.d.ts +1 -1
- package/dist/components/molecules/Tree/Tree.vue.d.ts +31 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +559 -230
- package/dist/nuxt.js +4 -0
- package/package.json +1 -1
- package/src/assets/svg/page_mockup_about.svg +27 -0
- package/src/assets/svg/page_mockup_introduction.svg +9 -0
- package/src/assets/svg/page_mockup_other.svg +13 -0
- package/src/assets/svg/page_mockup_packages.svg +34 -0
- package/src/assets/svg/page_mockup_references.svg +19 -0
- package/src/assets/svg/page_mockup_reviews.svg +46 -0
- package/src/assets/svg/page_mockup_services.svg +22 -0
- package/src/assets/svg/page_mockup_valuation.svg +24 -0
- package/src/components/molecules/PageCard/PageCard.vue +92 -0
- package/src/components/molecules/PageMockup/PageMockup.vue +52 -0
- package/src/components/molecules/PageTypeTile/PageTypeTile.vue +34 -0
- package/src/components/molecules/Tree/Tree.vue +342 -0
- package/src/components.json +4 -0
- package/src/index.ts +10 -0
- package/tailwind/nexxtmove.tailwind.custom.css +6 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed, nextTick, onMounted, ref, useTemplateRef, watch } from 'vue'
|
|
3
|
+
import { useResizeObserver } from '@vueuse/core'
|
|
4
|
+
import NexxtIcon, { type NexxtIconName } from '../../atoms/Icon/Icon.vue'
|
|
5
|
+
|
|
6
|
+
export interface NexxtTreeNode {
|
|
7
|
+
/** Unique within the tree; this is what `selected` and `collapsed` hold. */
|
|
8
|
+
id: string
|
|
9
|
+
label: string
|
|
10
|
+
icon?: NexxtIconName
|
|
11
|
+
/** A node with children gets a chevron and can be folded. */
|
|
12
|
+
children?: NexxtTreeNode[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface NexxtTreeProps {
|
|
16
|
+
/** The top-level nodes; nest `children` for deeper levels. */
|
|
17
|
+
nodes: NexxtTreeNode[]
|
|
18
|
+
/** Accessible name for the tree, e.g. the heading above it. */
|
|
19
|
+
label: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface Row {
|
|
23
|
+
node: NexxtTreeNode
|
|
24
|
+
depth: number
|
|
25
|
+
parentId: string | null
|
|
26
|
+
expandable: boolean
|
|
27
|
+
expanded: boolean
|
|
28
|
+
position: number
|
|
29
|
+
setSize: number
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
defineOptions({ name: 'NexxtTree' })
|
|
33
|
+
|
|
34
|
+
const props = defineProps<NexxtTreeProps>()
|
|
35
|
+
|
|
36
|
+
const selected = defineModel<string | null>('selected', { default: null })
|
|
37
|
+
/** Ids of the nodes whose children are hidden. Tracking the collapsed ones makes "everything open" the default. */
|
|
38
|
+
const collapsed = defineModel<string[]>('collapsed', { default: () => [] })
|
|
39
|
+
|
|
40
|
+
const INDENT = 22
|
|
41
|
+
|
|
42
|
+
const root = useTemplateRef<HTMLElement>('root')
|
|
43
|
+
const focusedId = ref<string | null>(null)
|
|
44
|
+
|
|
45
|
+
// The visible rows in document order; children of a collapsed node are left out
|
|
46
|
+
const rows = computed(() => {
|
|
47
|
+
const result: Row[] = []
|
|
48
|
+
|
|
49
|
+
const walk = (nodes: NexxtTreeNode[], depth: number, parentId: string | null) => {
|
|
50
|
+
nodes.forEach((node, index) => {
|
|
51
|
+
const expandable = !!node.children?.length
|
|
52
|
+
const expanded = expandable && !collapsed.value.includes(node.id)
|
|
53
|
+
|
|
54
|
+
result.push({
|
|
55
|
+
node,
|
|
56
|
+
depth,
|
|
57
|
+
parentId,
|
|
58
|
+
expandable,
|
|
59
|
+
expanded,
|
|
60
|
+
position: index + 1,
|
|
61
|
+
setSize: nodes.length,
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
if (expanded) walk(node.children!, depth + 1, node.id)
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
walk(props.nodes, 0, null)
|
|
69
|
+
|
|
70
|
+
return result
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// A row is only highlighted while it is actually on screen; collapsing its parent fades the
|
|
74
|
+
// highlight out rather than dropping it, so reopening slides it back from the same spot
|
|
75
|
+
const selectionVisible = computed(
|
|
76
|
+
() => !!selected.value && rows.value.some((row) => row.node.id === selected.value),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
/** Geometry of the moving highlight, relative to the tree container. */
|
|
80
|
+
const highlight = ref({ top: 0, height: 0 })
|
|
81
|
+
const highlightElement = useTemplateRef<HTMLElement>('highlightElement')
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* How the highlight travels to its next spot. A selection arrives on the row it was clicked on,
|
|
85
|
+
* so it decelerates; a row change drags the highlight along with a row that is sliding under
|
|
86
|
+
* the `move` transition, so it has to use that transition's duration and curve or the two come
|
|
87
|
+
* apart on screen. The first render and layout corrections do not travel at all.
|
|
88
|
+
*/
|
|
89
|
+
const MOTION = {
|
|
90
|
+
none: '',
|
|
91
|
+
select: 'transition-[transform,opacity] duration-200 ease-out-strong',
|
|
92
|
+
move: 'transition-[transform,opacity] duration-200 ease-in-out-strong',
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const motion = ref<keyof typeof MOTION>('none')
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Matched on the dataset rather than in a selector, so an id never needs escaping. A row that is
|
|
99
|
+
* leaving keeps its id for another beat, but it is out of the flow — `absolute` marks it — and
|
|
100
|
+
* folding the same node straight back open would otherwise find that stale one.
|
|
101
|
+
*/
|
|
102
|
+
const rowElement = (id: string | null | undefined) =>
|
|
103
|
+
Array.from(root.value?.querySelectorAll<HTMLElement>('[data-tree-id]') ?? []).find(
|
|
104
|
+
(element) => element.dataset.treeId === id && !element.classList.contains('absolute'),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
const updateHighlight = async () => {
|
|
108
|
+
await nextTick()
|
|
109
|
+
if (!root.value || !selectionVisible.value) return
|
|
110
|
+
|
|
111
|
+
const element = rowElement(selected.value)
|
|
112
|
+
|
|
113
|
+
if (element) highlight.value = { top: element.offsetTop, height: element.offsetHeight }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Reposition without animating, for the first render and for layout corrections. Reading the
|
|
118
|
+
* geometry makes the browser commit the new position while the transition is still off; the
|
|
119
|
+
* class coming back after that has nothing left to animate. Deliberately not `requestAnimationFrame`:
|
|
120
|
+
* that never fires while the tree sits in a hidden tab, which would leave it unable to animate.
|
|
121
|
+
*/
|
|
122
|
+
const jumpTo = async (reposition: () => Promise<void>) => {
|
|
123
|
+
const previous = motion.value
|
|
124
|
+
motion.value = 'none'
|
|
125
|
+
|
|
126
|
+
await reposition()
|
|
127
|
+
await nextTick()
|
|
128
|
+
root.value?.getBoundingClientRect()
|
|
129
|
+
|
|
130
|
+
motion.value = previous
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Rows above the selection can appear or disappear, so every row change is a reposition too
|
|
134
|
+
watch([selected, rows], ([id], [previousId]) => {
|
|
135
|
+
motion.value = id === previousId ? 'move' : 'select'
|
|
136
|
+
updateHighlight()
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
// A resize corrects the geometry — late webfonts, a narrower panel — so it never animates.
|
|
140
|
+
// Expanding and collapsing resizes the tree too, and there the highlight is mid-flight: forcing
|
|
141
|
+
// the geometry then cancels its transition and teleports it to the row's end position.
|
|
142
|
+
useResizeObserver(root, () => {
|
|
143
|
+
if (highlightElement.value?.getAnimations().length) return
|
|
144
|
+
|
|
145
|
+
jumpTo(updateHighlight)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
onMounted(() =>
|
|
149
|
+
jumpTo(async () => {
|
|
150
|
+
await updateHighlight()
|
|
151
|
+
// Fallback metrics put the rows at another height, which would animate once the real font lands
|
|
152
|
+
await document.fonts?.ready
|
|
153
|
+
await updateHighlight()
|
|
154
|
+
}),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Children unfold from under their parent. They are inserted at their final spot, where the rows
|
|
159
|
+
* below them are still sliding down to make room, so each one starts stacked on the parent's
|
|
160
|
+
* bottom edge and travels to its slot on the same duration and curve as the `move` transition.
|
|
161
|
+
* Fading in is what keeps the stack readable: on the way out from under the parent the children
|
|
162
|
+
* cover each other and the rows below, so the label only comes up over the last stretch, once
|
|
163
|
+
* everything has all but landed. Inline rather than in `enter-active-class`, because the two
|
|
164
|
+
* properties need their own timing.
|
|
165
|
+
*/
|
|
166
|
+
const ENTER_TRANSITION = 'transform 200ms var(--ease-in-out-strong), opacity 80ms linear 120ms'
|
|
167
|
+
|
|
168
|
+
const onEnter = (element: Element) => {
|
|
169
|
+
const row = element as HTMLElement
|
|
170
|
+
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return
|
|
171
|
+
|
|
172
|
+
const parent = rowElement(
|
|
173
|
+
rows.value.find((item) => item.node.id === row.dataset.treeId)?.parentId,
|
|
174
|
+
)
|
|
175
|
+
const travel = parent ? row.offsetTop - (parent.offsetTop + parent.offsetHeight) : 0
|
|
176
|
+
|
|
177
|
+
row.style.transition = 'none'
|
|
178
|
+
row.style.transform = `translateY(${-travel}px)`
|
|
179
|
+
row.style.opacity = '0'
|
|
180
|
+
// Nothing to click on a row that is still on its way and cannot be read yet
|
|
181
|
+
row.style.pointerEvents = 'none'
|
|
182
|
+
|
|
183
|
+
// Commits the start of the travel while the transition is off, so only the way back animates
|
|
184
|
+
row.getBoundingClientRect()
|
|
185
|
+
|
|
186
|
+
row.style.transition = ENTER_TRANSITION
|
|
187
|
+
row.style.transform = ''
|
|
188
|
+
row.style.opacity = ''
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const onEnterDone = (element: Element) => {
|
|
192
|
+
const row = element as HTMLElement
|
|
193
|
+
|
|
194
|
+
row.style.transition = ''
|
|
195
|
+
row.style.pointerEvents = ''
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// One tab stop for the whole tree: the focused row, else the selected one, else the first
|
|
199
|
+
const tabStopId = computed(() => {
|
|
200
|
+
const ids = rows.value.map((row) => row.node.id)
|
|
201
|
+
|
|
202
|
+
if (focusedId.value && ids.includes(focusedId.value)) return focusedId.value
|
|
203
|
+
if (selected.value && ids.includes(selected.value)) return selected.value
|
|
204
|
+
|
|
205
|
+
return ids[0] ?? null
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
const toggle = (id: string) => {
|
|
209
|
+
collapsed.value = collapsed.value.includes(id)
|
|
210
|
+
? collapsed.value.filter((item) => item !== id)
|
|
211
|
+
: [...collapsed.value, id]
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const focusRow = async (id: string) => {
|
|
215
|
+
focusedId.value = id
|
|
216
|
+
await nextTick()
|
|
217
|
+
|
|
218
|
+
// Matched on the dataset rather than in a selector, so an id never needs escaping
|
|
219
|
+
Array.from(root.value?.querySelectorAll<HTMLElement>('[data-tree-id]') ?? [])
|
|
220
|
+
.find((element) => element.dataset.treeId === id)
|
|
221
|
+
?.focus()
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const onKeydown = (event: KeyboardEvent) => {
|
|
225
|
+
const index = rows.value.findIndex((row) => row.node.id === tabStopId.value)
|
|
226
|
+
const row = rows.value[index]
|
|
227
|
+
if (!row) return
|
|
228
|
+
|
|
229
|
+
const move = (target: Row | undefined) => {
|
|
230
|
+
if (target) focusRow(target.node.id)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
switch (event.key) {
|
|
234
|
+
case 'ArrowDown':
|
|
235
|
+
move(rows.value[index + 1])
|
|
236
|
+
break
|
|
237
|
+
case 'ArrowUp':
|
|
238
|
+
move(rows.value[index - 1])
|
|
239
|
+
break
|
|
240
|
+
case 'Home':
|
|
241
|
+
move(rows.value[0])
|
|
242
|
+
break
|
|
243
|
+
case 'End':
|
|
244
|
+
move(rows.value[rows.value.length - 1])
|
|
245
|
+
break
|
|
246
|
+
case 'ArrowRight':
|
|
247
|
+
if (row.expandable && !row.expanded) toggle(row.node.id)
|
|
248
|
+
else if (row.expanded) move(rows.value[index + 1])
|
|
249
|
+
break
|
|
250
|
+
case 'ArrowLeft':
|
|
251
|
+
if (row.expanded) toggle(row.node.id)
|
|
252
|
+
else if (row.parentId) focusRow(row.parentId)
|
|
253
|
+
break
|
|
254
|
+
case 'Enter':
|
|
255
|
+
case ' ':
|
|
256
|
+
selected.value = row.node.id
|
|
257
|
+
break
|
|
258
|
+
default:
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
event.preventDefault()
|
|
263
|
+
}
|
|
264
|
+
</script>
|
|
265
|
+
|
|
266
|
+
<template>
|
|
267
|
+
<div ref="root" role="tree" :aria-label="label" class="relative isolate" @keydown="onKeydown">
|
|
268
|
+
<!-- One highlight for the whole tree: selecting another row slides it instead of
|
|
269
|
+
repainting two backgrounds. Behind the rows, so their hover still reads. -->
|
|
270
|
+
<div
|
|
271
|
+
ref="highlightElement"
|
|
272
|
+
aria-hidden="true"
|
|
273
|
+
class="pointer-events-none absolute top-0 left-0 -z-10 w-full rounded-md bg-cornflower-blue-50 motion-reduce:transition-none"
|
|
274
|
+
:class="[MOTION[motion], selectionVisible ? 'opacity-100' : 'opacity-0']"
|
|
275
|
+
:style="{ transform: `translateY(${highlight.top}px)`, height: `${highlight.height}px` }"
|
|
276
|
+
data-tree-highlight
|
|
277
|
+
/>
|
|
278
|
+
|
|
279
|
+
<!-- A `group` keeps the tree/treeitem relation intact now that the rows sit one level
|
|
280
|
+
deeper. Leaving rows go absolute so the remaining ones slide up right away, and go
|
|
281
|
+
invisible with them: out of the flow they stack on the first one's spot while the rows
|
|
282
|
+
below slide over them, which read as a flash of doubled labels. Folding away is not a
|
|
283
|
+
departure worth animating — the rows sliding up under the parent tell the story. -->
|
|
284
|
+
<TransitionGroup
|
|
285
|
+
tag="div"
|
|
286
|
+
role="group"
|
|
287
|
+
leave-active-class="invisible absolute w-full transition-none!"
|
|
288
|
+
move-class="transition-transform duration-200 ease-in-out-strong motion-reduce:transition-none"
|
|
289
|
+
@enter="onEnter"
|
|
290
|
+
@after-enter="onEnterDone"
|
|
291
|
+
@enter-cancelled="onEnterDone"
|
|
292
|
+
>
|
|
293
|
+
<!-- Every row keeps `small-normal`: switching the selected one to semibold widened its
|
|
294
|
+
label, which made the text jump. Colour carries the selection instead. -->
|
|
295
|
+
<div
|
|
296
|
+
v-for="row in rows"
|
|
297
|
+
:key="row.node.id"
|
|
298
|
+
role="treeitem"
|
|
299
|
+
:aria-level="row.depth + 1"
|
|
300
|
+
:aria-posinset="row.position"
|
|
301
|
+
:aria-setsize="row.setSize"
|
|
302
|
+
:aria-selected="selected === row.node.id"
|
|
303
|
+
:aria-expanded="row.expandable ? row.expanded : undefined"
|
|
304
|
+
:tabindex="row.node.id === tabStopId ? 0 : -1"
|
|
305
|
+
:data-tree-id="row.node.id"
|
|
306
|
+
class="group mb-0.5 flex cursor-pointer items-center gap-1.5 rounded-md py-1 pr-2 small-normal transition-colors duration-150 select-none focus-visible:outline-2 focus-visible:outline-cornflower-blue-500 motion-reduce:transition-none"
|
|
307
|
+
:class="
|
|
308
|
+
selected === row.node.id ? 'text-cornflower-blue-600' : 'text-gray-800 hover:bg-gray-100'
|
|
309
|
+
"
|
|
310
|
+
:style="{ paddingLeft: `${row.depth * INDENT + 4}px` }"
|
|
311
|
+
@click="selected = row.node.id"
|
|
312
|
+
@focus="focusedId = row.node.id"
|
|
313
|
+
>
|
|
314
|
+
<!-- Out of the tab order: the arrow keys expand and collapse for keyboard users.
|
|
315
|
+
Stays grey on the selected row: the chevron is a control, not part of the label. -->
|
|
316
|
+
<button
|
|
317
|
+
v-if="row.expandable"
|
|
318
|
+
type="button"
|
|
319
|
+
tabindex="-1"
|
|
320
|
+
aria-hidden="true"
|
|
321
|
+
class="flex size-5 shrink-0 cursor-pointer items-center justify-center text-gray-400 transition-colors duration-150 group-hover:text-gray-600 hover:text-gray-600 motion-reduce:transition-none"
|
|
322
|
+
@click.stop="toggle(row.node.id)"
|
|
323
|
+
>
|
|
324
|
+
<NexxtIcon
|
|
325
|
+
name="chevron-down"
|
|
326
|
+
class="extra-small-normal transition-transform duration-150 ease-out motion-reduce:transition-none"
|
|
327
|
+
:class="!row.expanded && '-rotate-90'"
|
|
328
|
+
/>
|
|
329
|
+
</button>
|
|
330
|
+
<span v-else class="size-5 shrink-0" />
|
|
331
|
+
|
|
332
|
+
<NexxtIcon
|
|
333
|
+
v-if="row.node.icon"
|
|
334
|
+
:name="row.node.icon"
|
|
335
|
+
class="shrink-0 transition-colors duration-150 motion-reduce:transition-none"
|
|
336
|
+
:class="selected === row.node.id ? 'text-cornflower-blue-600' : 'text-gray-600'"
|
|
337
|
+
/>
|
|
338
|
+
<span class="truncate">{{ row.node.label }}</span>
|
|
339
|
+
</div>
|
|
340
|
+
</TransitionGroup>
|
|
341
|
+
</div>
|
|
342
|
+
</template>
|
package/src/components.json
CHANGED
|
@@ -25,7 +25,10 @@
|
|
|
25
25
|
"NexxtInputField": "components/molecules/InputField/InputField.vue",
|
|
26
26
|
"NexxtInputSelect": "components/molecules/InputSelect/InputSelect.vue",
|
|
27
27
|
"NexxtModalFooter": "components/molecules/ModalFooter/ModalFooter.vue",
|
|
28
|
+
"NexxtPageCard": "components/molecules/PageCard/PageCard.vue",
|
|
28
29
|
"NexxtPageContainer": "components/molecules/PageContainer/PageContainer.vue",
|
|
30
|
+
"NexxtPageMockup": "components/molecules/PageMockup/PageMockup.vue",
|
|
31
|
+
"NexxtPageTypeTile": "components/molecules/PageTypeTile/PageTypeTile.vue",
|
|
29
32
|
"NexxtPagination": "components/molecules/Pagination/Pagination.vue",
|
|
30
33
|
"NexxtPropertyCard": "components/molecules/PropertyCard/PropertyCard.vue",
|
|
31
34
|
"NexxtSelect": "components/molecules/Select/Select.vue",
|
|
@@ -43,6 +46,7 @@
|
|
|
43
46
|
"NexxtTimelineLanes": "components/molecules/TimelineLanes/TimelineLanes.vue",
|
|
44
47
|
"NexxtToast": "components/molecules/Toast/Toast.vue",
|
|
45
48
|
"NexxtToggleGroup": "components/molecules/ToggleGroup/ToggleGroup.vue",
|
|
49
|
+
"NexxtTree": "components/molecules/Tree/Tree.vue",
|
|
46
50
|
"NexxtCalendar": "components/organisms/Calendar/Calendar.vue",
|
|
47
51
|
"NexxtColorPicker": "components/organisms/ColorPicker/ColorPicker.vue",
|
|
48
52
|
"NexxtCombobox": "components/organisms/Combobox/Combobox.vue",
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,12 @@ export type { EnergyLabelLetter } from './components/atoms/EnergyLabel/EnergyLab
|
|
|
13
13
|
export type { IconType, NexxtIconName } from './components/atoms/Icon/Icon.vue'
|
|
14
14
|
export type { CustomIconName } from './components/atoms/Icon/customIcons'
|
|
15
15
|
export type { NexxtTabItem } from './components/molecules/Tabs/Tabs.vue'
|
|
16
|
+
export type {
|
|
17
|
+
NexxtPageCardProps,
|
|
18
|
+
NexxtPageCardSlots,
|
|
19
|
+
} from './components/molecules/PageCard/PageCard.vue'
|
|
20
|
+
export type { NexxtPageMockupType } from './components/molecules/PageMockup/PageMockup.vue'
|
|
21
|
+
export type { NexxtTreeNode, NexxtTreeProps } from './components/molecules/Tree/Tree.vue'
|
|
16
22
|
export type {
|
|
17
23
|
EditorAlignItemsMessages,
|
|
18
24
|
EditorAlignItemsValue,
|
|
@@ -95,7 +101,10 @@ export { default as NexxtInputSelect } from './components/molecules/InputSelect/
|
|
|
95
101
|
export { default as NexxtLoader } from './components/atoms/Loader/Loader.vue'
|
|
96
102
|
export { default as NexxtModal } from './components/organisms/Modal/Modal.vue'
|
|
97
103
|
export { default as NexxtModalFooter } from './components/molecules/ModalFooter/ModalFooter.vue'
|
|
104
|
+
export { default as NexxtPageCard } from './components/molecules/PageCard/PageCard.vue'
|
|
98
105
|
export { default as NexxtPageContainer } from './components/molecules/PageContainer/PageContainer.vue'
|
|
106
|
+
export { default as NexxtPageMockup } from './components/molecules/PageMockup/PageMockup.vue'
|
|
107
|
+
export { default as NexxtPageTypeTile } from './components/molecules/PageTypeTile/PageTypeTile.vue'
|
|
99
108
|
export { default as NexxtPagination } from './components/molecules/Pagination/Pagination.vue'
|
|
100
109
|
export { default as NexxtPresenceIndicator } from './components/atoms/PresenceIndicator/PresenceIndicator.vue'
|
|
101
110
|
export { default as NexxtProgressBar } from './components/organisms/ProgressBar/ProgressBar.vue'
|
|
@@ -129,4 +138,5 @@ export { default as NexxtToast } from './components/molecules/Toast/Toast.vue'
|
|
|
129
138
|
export { default as NexxtToaster } from './components/organisms/Toaster/Toaster.vue'
|
|
130
139
|
export { default as NexxtToggleGroup } from './components/molecules/ToggleGroup/ToggleGroup.vue'
|
|
131
140
|
export { default as NexxtTooltip } from './components/atoms/Tooltip/Tooltip.vue'
|
|
141
|
+
export { default as NexxtTree } from './components/molecules/Tree/Tree.vue'
|
|
132
142
|
export { default as NexxtVideoPlayer } from './components/organisms/VideoPlayer/VideoPlayer.vue'
|
|
@@ -49,4 +49,10 @@
|
|
|
49
49
|
|
|
50
50
|
--spacing-8xl: 100rem;
|
|
51
51
|
--spacing-full-width: 116rem; /* 1920px (full hd) minus the sidebar: max app width */
|
|
52
|
+
|
|
53
|
+
/* Motion easings. `ease-out-strong` decelerates hard: things that arrive or
|
|
54
|
+
settle into place. `ease-in-out-strong` for elements that move from one
|
|
55
|
+
spot to another. */
|
|
56
|
+
--ease-out-strong: cubic-bezier(0.23, 1, 0.32, 1);
|
|
57
|
+
--ease-in-out-strong: cubic-bezier(0.77, 0, 0.175, 1);
|
|
52
58
|
}
|