@txstate-mws/svelte-components 1.0.4 → 1.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/.eslintrc.cjs +37 -0
- package/docs/CollapsingTable.md +79 -0
- package/docs/ConditionalWrapper.md +35 -0
- package/docs/Modal.md +63 -0
- package/docs/PopupMenu.md +31 -0
- package/docs/ScreenReaderOnly.md +10 -0
- package/docs/actions.md +84 -0
- package/docs/stores.md +203 -0
- package/package.json +15 -37
- package/playwright.config.js +112 -0
- package/src/app.html +14 -0
- package/src/global.d.ts +1 -0
- package/src/lib/actions/buttonify.ts +27 -0
- package/src/lib/actions/glue.ts +96 -0
- package/src/lib/actions/index.ts +7 -0
- package/src/lib/actions/offset.ts +43 -0
- package/src/lib/actions/portal.ts +13 -0
- package/src/lib/actions/resize.ts +72 -0
- package/src/lib/actions/sticky.ts +105 -0
- package/src/lib/actions/stickyfixed.ts +160 -0
- package/src/lib/components/Card.svelte +25 -0
- package/src/lib/components/CardLayout.svelte +188 -0
- package/{components → src/lib/components}/CollapsingTable.svelte +65 -63
- package/{components → src/lib/components}/ConditionalWrapper.svelte +7 -6
- package/src/lib/components/FocusLock.svelte +102 -0
- package/{components → src/lib/components}/Modal.svelte +24 -22
- package/src/lib/components/MultiSelect.svelte +174 -0
- package/src/lib/components/PopupMenu.svelte +285 -0
- package/{components → src/lib/components}/ScreenReaderOnly.svelte +4 -3
- package/src/lib/components/index.ts +9 -0
- package/src/lib/index.ts +4 -0
- package/src/lib/types/CardLayout.ts +14 -0
- package/src/lib/types/CollapsingTable.ts +8 -0
- package/src/lib/types/PopupMenu.ts +5 -0
- package/src/lib/types/index.ts +3 -0
- package/src/lib/util/bodyoffset.ts +77 -0
- package/src/lib/util/index.ts +3 -0
- package/src/lib/util/repositioned.ts +91 -0
- package/src/lib/util/util.ts +36 -0
- package/src/routes/__layout.svelte +125 -0
- package/src/routes/actions/index.svelte +1 -0
- package/src/routes/actions/offset.svelte +4 -0
- package/src/routes/actions/offsetdebounce.svelte +4 -0
- package/src/routes/actions/resize.svelte +4 -0
- package/src/routes/actions/resizedebounce.svelte +4 -0
- package/src/routes/actions/sticky.svelte +4 -0
- package/src/routes/cards/index.svelte +4 -0
- package/src/routes/cards/preserve.svelte +4 -0
- package/src/routes/collapsingtable/index.svelte +4 -0
- package/src/routes/collapsingtable/stickyheader.svelte +4 -0
- package/src/routes/index.svelte +2 -0
- package/src/routes/modal.svelte +4 -0
- package/src/routes/multiselect.svelte +4 -0
- package/src/routes/popupmenu/body.svelte +4 -0
- package/src/routes/popupmenu/complex.svelte +4 -0
- package/src/routes/popupmenu/empty.svelte +5 -0
- package/src/routes/popupmenu/index.svelte +4 -0
- package/src/routes/popupmenu/parent.svelte +4 -0
- package/src/routes/popupmenu/scrollable.svelte +27 -0
- package/src/routes/popupmenu/showselected.svelte +4 -0
- package/src/test/Cards.svelte +64 -0
- package/src/test/CollapsingTableTest.svelte +36 -0
- package/src/test/ModalTest.svelte +58 -0
- package/src/test/MultiSelectTest.svelte +26 -0
- package/src/test/Offset.svelte +32 -0
- package/src/test/PopupMenuBody.svelte +34 -0
- package/src/test/PopupMenuButtonIsParent.svelte +36 -0
- package/src/test/PopupMenuComplex.svelte +34 -0
- package/src/test/PopupMenuSimple.svelte +36 -0
- package/src/test/Resize.svelte +29 -0
- package/src/test/Sticky.svelte +65 -0
- package/static/layout.css +263 -0
- package/svelte.config.js +15 -0
- package/tests/.eslintrc.cjs +19 -0
- package/tests/CardLayout.test.js +41 -0
- package/tests/CollapsingTable.test.js +37 -0
- package/tests/Modal.test.js +18 -0
- package/tests/MultiSelect.test.js +58 -0
- package/tests/PopupMenu.test.js +55 -0
- package/tests/common.js +4 -0
- package/tsconfig.json +31 -0
- package/actions/buttonify.d.ts +0 -8
- package/actions/buttonify.js +0 -26
- package/actions/glue.d.ts +0 -16
- package/actions/glue.js +0 -82
- package/actions/index.d.ts +0 -7
- package/actions/index.js +0 -7
- package/actions/offset.d.ts +0 -15
- package/actions/offset.js +0 -35
- package/actions/portal.d.ts +0 -4
- package/actions/portal.js +0 -13
- package/actions/resize.d.ts +0 -20
- package/actions/resize.js +0 -45
- package/actions/sticky.d.ts +0 -33
- package/actions/sticky.js +0 -85
- package/actions/stickyfixed.d.ts +0 -25
- package/actions/stickyfixed.js +0 -157
- package/components/CollapsingTable.svelte.d.ts +0 -59
- package/components/ConditionalWrapper.svelte.d.ts +0 -23
- package/components/FocusLock.svelte +0 -107
- package/components/FocusLock.svelte.d.ts +0 -26
- package/components/Modal.svelte.d.ts +0 -27
- package/components/PopupMenu.svelte +0 -279
- package/components/PopupMenu.svelte.d.ts +0 -44
- package/components/ScreenReaderOnly.svelte.d.ts +0 -20
- package/components/index.d.ts +0 -6
- package/components/index.js +0 -6
- package/index.d.ts +0 -4
- package/index.js +0 -4
- package/types/CollapsingTable.d.ts +0 -8
- package/types/CollapsingTable.js +0 -1
- package/types/PopupMenu.d.ts +0 -5
- package/types/PopupMenu.js +0 -1
- package/types/index.d.ts +0 -2
- package/types/index.js +0 -2
- package/util/bodyoffset.d.ts +0 -14
- package/util/bodyoffset.js +0 -72
- package/util/index.d.ts +0 -3
- package/util/index.js +0 -3
- package/util/repositioned.d.ts +0 -12
- package/util/repositioned.js +0 -81
- package/util/util.d.ts +0 -2
- package/util/util.js +0 -10
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { equal, isNotNull, randomid, toArray } from 'txstate-utils'
|
|
2
|
+
import { bodyOffset, targetOffset } from './bodyoffset.js'
|
|
3
|
+
import type { ElementOffsets } from './bodyoffset.js'
|
|
4
|
+
|
|
5
|
+
const observers = new Map<string, () => void>()
|
|
6
|
+
let mutationobserver: MutationObserver
|
|
7
|
+
export function watchForMutations (cb: () => void) {
|
|
8
|
+
function watch () {
|
|
9
|
+
for (const cb of observers.values()) cb()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (observers.size === 0) {
|
|
13
|
+
mutationobserver ??= new MutationObserver(watch)
|
|
14
|
+
window.addEventListener('resize', watch, { passive: true })
|
|
15
|
+
mutationobserver.observe(document.body, {
|
|
16
|
+
subtree: true,
|
|
17
|
+
childList: true,
|
|
18
|
+
attributes: true,
|
|
19
|
+
characterData: true
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const id = randomid()
|
|
24
|
+
observers.set(id, cb)
|
|
25
|
+
cb()
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
destroy () {
|
|
29
|
+
observers.delete(id)
|
|
30
|
+
if (observers.size === 0) {
|
|
31
|
+
window.removeEventListener('resize', watch)
|
|
32
|
+
mutationobserver.disconnect()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function watchForPositionChange (el: (HTMLElement|undefined)[]|HTMLElement|undefined, cb: ((offset: Required<ElementOffsets>) => void)|((offsets: Required<ElementOffsets>[]) => void)) {
|
|
39
|
+
let lastoffsets: ElementOffsets[] = []
|
|
40
|
+
let els = toArray(el).filter(isNotNull)
|
|
41
|
+
function watch () {
|
|
42
|
+
const offsets = els.map(bodyOffset)
|
|
43
|
+
if (offsets.some((offset, i) => !equal(offset, lastoffsets[i]))) {
|
|
44
|
+
lastoffsets = offsets
|
|
45
|
+
Array.isArray(el) ? (cb as any)(offsets) : (cb as any)(offsets[0])
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const { destroy } = watchForMutations(watch)
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
update (newEl: (HTMLElement|undefined)[]|HTMLElement|undefined, newCb: ((offset: Required<ElementOffsets>) => void)|((offsets: Required<ElementOffsets>[]) => void)) {
|
|
53
|
+
const changed = newEl !== el || newCb !== cb
|
|
54
|
+
if (changed) {
|
|
55
|
+
el = newEl
|
|
56
|
+
els = toArray(el).filter(isNotNull)
|
|
57
|
+
cb = newCb
|
|
58
|
+
watch()
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
destroy
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function watchForPositionChangeInContainer (el: HTMLElement|undefined, container: HTMLElement|undefined, cb: (offset: Required<ElementOffsets>) => void) {
|
|
66
|
+
let lastoffset: ElementOffsets
|
|
67
|
+
|
|
68
|
+
function watch () {
|
|
69
|
+
if (!el || !container) return
|
|
70
|
+
const offset = targetOffset(el, container)
|
|
71
|
+
if (!equal(offset, lastoffset)) {
|
|
72
|
+
lastoffset = offset
|
|
73
|
+
cb(offset)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const { destroy } = watchForMutations(watch)
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
update (newEl: HTMLElement|undefined, newContainer: HTMLElement|undefined, newCb: (offset: Required<ElementOffsets>) => void) {
|
|
81
|
+
const changed = newEl !== el || newContainer !== container || newCb !== cb
|
|
82
|
+
if (changed) {
|
|
83
|
+
el = newEl
|
|
84
|
+
container = newContainer
|
|
85
|
+
cb = newCb
|
|
86
|
+
watch()
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
destroy
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function classes (...classNames: (string | undefined)[]) {
|
|
2
|
+
return classNames.filter(Boolean).join(' ')
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function debounced (fn: Function, timeout: number) {
|
|
6
|
+
let timer: NodeJS.Timeout
|
|
7
|
+
return (...args: any[]) => {
|
|
8
|
+
clearTimeout(timer)
|
|
9
|
+
timer = setTimeout(() => fn(...args), timeout)
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function debouncedPromise <T> (fn: (..._: any) => Promise<T>|T, timeout: number, defaultValue: T): (..._: any) => Promise<T> {
|
|
14
|
+
let version = 0
|
|
15
|
+
return async (...args: any[]) => {
|
|
16
|
+
const myversion = ++version
|
|
17
|
+
return await new Promise(resolve => {
|
|
18
|
+
setTimeout(() => {
|
|
19
|
+
if (version === myversion) {
|
|
20
|
+
version = 0
|
|
21
|
+
resolve(fn(...args))
|
|
22
|
+
} else {
|
|
23
|
+
resolve(defaultValue)
|
|
24
|
+
}
|
|
25
|
+
}, timeout)
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function modifierKey (e) {
|
|
31
|
+
return e.ctrlKey || e.altKey || e.metaKey || e.key === 'Insert'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function selectionIsLeft (ele: HTMLInputElement|HTMLTextAreaElement) {
|
|
35
|
+
return ele.selectionStart === 0 && ele.selectionEnd === 0
|
|
36
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { page } from '$app/stores'
|
|
3
|
+
interface Link {
|
|
4
|
+
label: string
|
|
5
|
+
href: string
|
|
6
|
+
subtitle?: string
|
|
7
|
+
sublinks?: Link[]
|
|
8
|
+
selected?: boolean
|
|
9
|
+
parent?: boolean
|
|
10
|
+
}
|
|
11
|
+
let links: Link[] = [
|
|
12
|
+
{ label: 'Home', href: '/', subtitle: 'Overview of everything svelte-components provides.' },
|
|
13
|
+
{
|
|
14
|
+
label: 'Actions',
|
|
15
|
+
href: '/actions',
|
|
16
|
+
subtitle: 'A generic component for presenting a dropdown menu.',
|
|
17
|
+
sublinks: [
|
|
18
|
+
{ label: 'offset', href: '/actions/offset' },
|
|
19
|
+
{ label: 'offset debounced', href: '/actions/offsetdebounce' },
|
|
20
|
+
{ label: 'resize', href: '/actions/resize' },
|
|
21
|
+
{ label: 'resize debounced', href: '/actions/resizedebounce' },
|
|
22
|
+
{ label: 'sticky', href: '/actions/sticky' }
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{ label: 'Modal', href: '/modal', subtitle: 'A component for accessible modal dialogs.' },
|
|
26
|
+
{
|
|
27
|
+
label: 'PopupMenu',
|
|
28
|
+
href: '/popupmenu',
|
|
29
|
+
subtitle: 'A generic component for presenting a dropdown menu.',
|
|
30
|
+
sublinks: [
|
|
31
|
+
{ label: 'show selected', href: '/popupmenu/showselected' },
|
|
32
|
+
{ label: 'complex placement', href: '/popupmenu/complex' },
|
|
33
|
+
{ label: 'body placement', href: '/popupmenu/body' },
|
|
34
|
+
{ label: 'button is parent', href: '/popupmenu/parent' },
|
|
35
|
+
{ label: 'scrollable', href: '/popupmenu/scrollable' },
|
|
36
|
+
{ label: 'no items', href: '/popupmenu/empty' }
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
label: 'CollapsingTable',
|
|
41
|
+
href: '/collapsingtable',
|
|
42
|
+
subtitle: 'A table that hides columns on small screens and introduces a dropdown to show one at a time.',
|
|
43
|
+
sublinks: [
|
|
44
|
+
{ label: 'sticky header', href: '/collapsingtable/stickyheader' }
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
label: 'Cards',
|
|
49
|
+
href: '/cards',
|
|
50
|
+
subtitle: 'Components for generating a pinterest-style card layout.',
|
|
51
|
+
sublinks: [
|
|
52
|
+
{ label: 'preserve order', href: '/cards/preserve' }
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
{ label: 'MultiSelect', href: '/multiselect', subtitle: 'A component for selecting multiple items from a list.' }
|
|
56
|
+
]
|
|
57
|
+
$: current = links.find(l => l.href === '/' ? $page.url.pathname === '/' : $page.url.pathname.startsWith(l.href))
|
|
58
|
+
$: title = current?.label
|
|
59
|
+
$: subtitle = current?.subtitle
|
|
60
|
+
|
|
61
|
+
function isSelected (link: Link) {
|
|
62
|
+
return $page.url.pathname === link.href && link.href !== '/'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isParentOfSelected (link: Link) {
|
|
66
|
+
return !isSelected(link) && link.href !== '/' && $page.url.pathname.startsWith(link.href)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function updateLinks (links: Link[], ..._: any) {
|
|
70
|
+
if (!links) return []
|
|
71
|
+
return links.map(l => ({ ...l, selected: isSelected(l), parent: isParentOfSelected(l), sublinks: updateLinks(l.sublinks) }))
|
|
72
|
+
}
|
|
73
|
+
$: links = updateLinks(links, $page)
|
|
74
|
+
|
|
75
|
+
let active = false
|
|
76
|
+
function onClick () {
|
|
77
|
+
active = !active
|
|
78
|
+
}
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<div id="layout" class:active>
|
|
82
|
+
<!-- Menu toggle -->
|
|
83
|
+
<a role="button" id="menuLink" class="menu-link" on:click={onClick}>
|
|
84
|
+
<!-- Hamburger icon -->
|
|
85
|
+
<span></span>
|
|
86
|
+
</a>
|
|
87
|
+
|
|
88
|
+
<div id="menu">
|
|
89
|
+
<div class="pure-menu">
|
|
90
|
+
<ul class="pure-menu-list">
|
|
91
|
+
{#each links as link}
|
|
92
|
+
<li class="pure-menu-item pure-menu-parent" class:pure-menu-selected={link.selected} class:pure-menu-subselected={link.parent}>
|
|
93
|
+
<a href="{link.href}" class="pure-menu-link">{link.label}</a>
|
|
94
|
+
</li>
|
|
95
|
+
{#if link.sublinks && link.sublinks.length && (link.selected || link.parent)}
|
|
96
|
+
<ul class="pure-menu-list">
|
|
97
|
+
{#each link.sublinks as sublink}
|
|
98
|
+
<li class="pure-menu-item" class:pure-menu-selected={sublink.selected}>
|
|
99
|
+
<a href="{sublink.href}" class="pure-menu-link">{sublink.label}</a>
|
|
100
|
+
</li>
|
|
101
|
+
{/each}
|
|
102
|
+
</ul>
|
|
103
|
+
{/if}
|
|
104
|
+
{/each}
|
|
105
|
+
</ul>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div id="main">
|
|
110
|
+
<div class="header">
|
|
111
|
+
<h1>{title}</h1>
|
|
112
|
+
{#if subtitle}<h2>{subtitle}</h2>{/if}
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div class="content">
|
|
116
|
+
<slot />
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<style>
|
|
122
|
+
#menuLink {
|
|
123
|
+
cursor: pointer;
|
|
124
|
+
}
|
|
125
|
+
</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
See menu at left.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import PopupMenuComplex from '../../test/PopupMenuComplex.svelte'
|
|
3
|
+
const items = [
|
|
4
|
+
{ value: '0', label: 'Sunday', disabled: true },
|
|
5
|
+
{ value: '1', label: 'Monday' },
|
|
6
|
+
{ value: '2', label: 'Tuesday' },
|
|
7
|
+
{ value: '3', label: 'Wednesday' },
|
|
8
|
+
{ value: '4', label: 'Thursday', disabled: true },
|
|
9
|
+
{ value: '5', label: 'Friday' },
|
|
10
|
+
{ value: '6', label: 'Saturday', disabled: true },
|
|
11
|
+
{ value: '7', label: 'Sunday', disabled: true },
|
|
12
|
+
{ value: '8', label: 'Monday' },
|
|
13
|
+
{ value: '9', label: 'Tuesday' },
|
|
14
|
+
{ value: '10', label: 'Wednesday' },
|
|
15
|
+
{ value: '11', label: 'Thursday', disabled: true },
|
|
16
|
+
{ value: '12', label: 'Friday' },
|
|
17
|
+
{ value: '13', label: 'Saturday', disabled: true },
|
|
18
|
+
{ value: '14', label: 'Sunday', disabled: true },
|
|
19
|
+
{ value: '15', label: 'Monday' },
|
|
20
|
+
{ value: '16', label: 'Tuesday' },
|
|
21
|
+
{ value: '17', label: 'Wednesday' },
|
|
22
|
+
{ value: '18', label: 'Thursday', disabled: true },
|
|
23
|
+
{ value: '19', label: 'Friday' },
|
|
24
|
+
{ value: '20', label: 'Saturday', disabled: true }
|
|
25
|
+
]
|
|
26
|
+
</script>
|
|
27
|
+
<PopupMenuComplex {items}></PopupMenuComplex>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import CardLayout from '$lib/components/CardLayout.svelte'
|
|
3
|
+
import Card from '$lib/components/Card.svelte'
|
|
4
|
+
export let maxwidth = 300
|
|
5
|
+
export let className = 'test1'
|
|
6
|
+
export let preserveorder = false
|
|
7
|
+
let cards = [
|
|
8
|
+
{ ptop: 30 },
|
|
9
|
+
{ ptop: 50 },
|
|
10
|
+
{ ptop: 20 },
|
|
11
|
+
{ ptop: 100 },
|
|
12
|
+
{ ptop: 40 },
|
|
13
|
+
{ ptop: 56.25 },
|
|
14
|
+
{ ptop: 150 },
|
|
15
|
+
{ ptop: 80 },
|
|
16
|
+
{ ptop: 10 }
|
|
17
|
+
]
|
|
18
|
+
function randheight () {
|
|
19
|
+
return Math.ceil(Math.random() * 150) + 20
|
|
20
|
+
}
|
|
21
|
+
function mutateall () {
|
|
22
|
+
cards = cards.map(_ => ({ ptop: randheight() }))
|
|
23
|
+
}
|
|
24
|
+
function mutatecard () {
|
|
25
|
+
const which = Math.floor(Math.random() * cards.length)
|
|
26
|
+
cards[which].ptop = randheight()
|
|
27
|
+
}
|
|
28
|
+
function addcard () {
|
|
29
|
+
cards = [...cards, { ptop: randheight() }]
|
|
30
|
+
}
|
|
31
|
+
function add20 () {
|
|
32
|
+
const newcards = []
|
|
33
|
+
for (let i = 0; i < 20; i++) newcards.push({ ptop: randheight() })
|
|
34
|
+
cards = [...cards, ...newcards]
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<CardLayout className={className} maxwidth={maxwidth} preserveorder={preserveorder}>
|
|
39
|
+
{#each cards as card, i}
|
|
40
|
+
<Card><div class="card card{i + 1}" style:padding-top={card.ptop + '%'} tabindex=0><span>Card {i + 1}</span></div></Card>
|
|
41
|
+
{/each}
|
|
42
|
+
</CardLayout>
|
|
43
|
+
|
|
44
|
+
<button on:click={mutateall}>Randomize All</button>
|
|
45
|
+
<button on:click={mutatecard}>Randomize Card</button>
|
|
46
|
+
<button on:click={addcard}>Add Card</button>
|
|
47
|
+
<button on:click={add20}>Add 20 Cards</button>
|
|
48
|
+
|
|
49
|
+
<style>
|
|
50
|
+
.card {
|
|
51
|
+
position: relative;
|
|
52
|
+
background-color: rgba(0, 0, 0, 0.3);
|
|
53
|
+
transition: padding-top 0.5s;
|
|
54
|
+
}
|
|
55
|
+
.card:focus {
|
|
56
|
+
outline: 2px solid blue;
|
|
57
|
+
}
|
|
58
|
+
.card span {
|
|
59
|
+
position: absolute;
|
|
60
|
+
top: 50%;
|
|
61
|
+
left: 50%;
|
|
62
|
+
transform: translate(-50%,-50%);
|
|
63
|
+
}
|
|
64
|
+
</style>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import CollapsingTable from '$lib/components/CollapsingTable.svelte'
|
|
3
|
+
import type { CollapsingTableColumn } from '$lib/types'
|
|
4
|
+
export let stickyheader = false
|
|
5
|
+
const items = [
|
|
6
|
+
{ id: 1, name: 'John Smith', age: 42, one: 1, two: 2, three: 3, four: 4, five: 5, six: 6 },
|
|
7
|
+
{ id: 3, name: 'Mary Sue', age: 23, one: 1, two: 2, three: 3, four: 4, five: 5, six: 6 },
|
|
8
|
+
{ id: 6, name: 'Billy Bob', age: 12, one: 1, two: 2, three: 3, four: 4, five: 5, six: 6 }
|
|
9
|
+
]
|
|
10
|
+
const config: CollapsingTableColumn[] = [
|
|
11
|
+
{ key: 'id' },
|
|
12
|
+
{ key: 'name', neverhide: true },
|
|
13
|
+
{ key: 'age' },
|
|
14
|
+
{ key: 'one' },
|
|
15
|
+
{ key: 'two' },
|
|
16
|
+
{ key: 'three' },
|
|
17
|
+
{ key: 'four', bodyCellClass: 'four' },
|
|
18
|
+
{ key: 'five' },
|
|
19
|
+
{ key: 'six' }
|
|
20
|
+
]
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<div class="before"></div>
|
|
24
|
+
<CollapsingTable {items} {config} {stickyheader} let:key let:value>
|
|
25
|
+
{#if key === 'five'}
|
|
26
|
+
<span class='test'>{value}</span>
|
|
27
|
+
{:else}
|
|
28
|
+
{value}
|
|
29
|
+
{/if}
|
|
30
|
+
</CollapsingTable>
|
|
31
|
+
<div class="after"></div>
|
|
32
|
+
|
|
33
|
+
<style>
|
|
34
|
+
.before { height: 40px; }
|
|
35
|
+
.after { height: 1000px; }
|
|
36
|
+
</style>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Modal from '$lib/components/Modal.svelte'
|
|
3
|
+
let shown = true
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
{#if shown}
|
|
7
|
+
<Modal on:escape={() => { shown = false }} >
|
|
8
|
+
<form>
|
|
9
|
+
<div class="formfield">
|
|
10
|
+
<label for="firstname">First Name:</label><input id="firstname" type="text" name="firstname">
|
|
11
|
+
</div>
|
|
12
|
+
<div class="formfield">
|
|
13
|
+
<label for="middlename">Middle Name:</label><input id="middlename" type="text" name="middlename">
|
|
14
|
+
</div>
|
|
15
|
+
<div class="formfield">
|
|
16
|
+
<label for="lastname">Last Name:</label><input id="lastname" type="text" name="lastname">
|
|
17
|
+
</div>
|
|
18
|
+
<input type="submit" value="Submit">
|
|
19
|
+
</form>
|
|
20
|
+
</Modal>
|
|
21
|
+
{/if}
|
|
22
|
+
<div class="content">
|
|
23
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer tempus nulla ut nulla auctor, quis sodales massa laoreet. Duis erat lacus, gravida vitae massa vel, semper accumsan urna. Morbi gravida dui sit amet sodales luctus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum eget pulvinar dui. Vivamus quis eros eu ex semper consectetur nec at elit. Praesent faucibus arcu a nisi semper, eu tempus felis vehicula. Suspendisse potenti.</p>
|
|
24
|
+
|
|
25
|
+
<p>Donec luctus nulla mattis ipsum aliquam imperdiet. Vestibulum nec finibus enim. Proin et arcu arcu. Quisque feugiat leo velit, vitae mollis felis ornare sed. Suspendisse sollicitudin, libero sed ullamcorper finibus, diam turpis eleifend velit, ut suscipit orci mauris id purus. Donec dapibus facilisis mauris a venenatis. Maecenas gravida velit maximus nibh tincidunt, eget auctor tellus efficitur. Aenean varius tristique lacus, sit amet placerat sem tempor at. Sed imperdiet fermentum nibh, sit amet tempor est cursus ac.</p>
|
|
26
|
+
<button id="test-button" on:click={() => { shown = true }}>Show Modal</button>
|
|
27
|
+
<p>Praesent augue magna, maximus at mattis eget, euismod nec neque. Phasellus ut dolor a turpis molestie fermentum. Phasellus condimentum, ipsum sed rutrum maximus, diam ex ultrices sapien, quis iaculis nisi arcu at turpis. Morbi aliquam mattis posuere. Integer pretium diam diam, non lobortis quam consectetur laoreet. Sed posuere blandit venenatis. Nulla gravida maximus arcu non consectetur.</p>
|
|
28
|
+
|
|
29
|
+
<p>Cras egestas massa mattis dictum imperdiet. Nulla bibendum nisl ipsum, ut scelerisque arcu consectetur vitae. Integer convallis id nunc sit amet faucibus. Maecenas suscipit diam orci, ac suscipit dolor convallis sed. Mauris vel tellus eget nunc mattis faucibus sit amet sed lorem. Cras lobortis, massa at vestibulum dignissim, quam tellus sagittis purus, eget dictum dolor quam in leo. Aliquam condimentum, ante sit amet semper laoreet, lacus est scelerisque mi, nec mollis diam massa et massa. Donec sagittis massa quis diam dictum, sit amet convallis mauris tincidunt. Nam vulputate ligula quis ex condimentum, in sollicitudin dolor accumsan. Donec volutpat dui in faucibus tempus. Proin vitae tincidunt ex. Proin ut ultricies diam. Praesent sed purus in purus efficitur fermentum vulputate quis dolor. Morbi viverra quam ac dolor facilisis consectetur.</p>
|
|
30
|
+
|
|
31
|
+
<p>Phasellus convallis congue mi, at auctor neque facilisis vestibulum. Maecenas tincidunt eros nec ornare vehicula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aenean odio erat, efficitur sit amet neque ut, condimentum scelerisque enim. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis sagittis nisl aliquam, rutrum eros eget, luctus augue. Praesent sollicitudin facilisis nisi, eget tincidunt dolor vestibulum sed. Nullam auctor consequat est, sit amet convallis sem fermentum vitae. Pellentesque tempor, turpis vel tristique imperdiet, tellus nunc blandit lorem, et tristique velit dui sed ante. Praesent tempor, sem at vulputate porta, dolor lacus condimentum augue, non tincidunt magna sapien ultrices massa. In consequat, ligula ac elementum ornare, enim dui semper metus, a vehicula arcu odio at sapien. Donec et sodales dui, sed tempus turpis.</p>
|
|
32
|
+
|
|
33
|
+
<p>Donec quis elit dolor. Proin rutrum lorem sapien, eu tempor urna commodo facilisis. Nunc at est eu purus mattis auctor eget et libero. In auctor dictum massa, nec hendrerit augue facilisis a. Vestibulum sit amet accumsan dolor, sit amet porta ante. Nam fringilla, velit eu tempus pulvinar, magna nibh iaculis dolor, a rutrum nibh turpis vel metus. Mauris quis lorem et nunc blandit feugiat. Phasellus vitae luctus turpis, id blandit nulla. Aliquam imperdiet mattis ante laoreet ullamcorper. Vivamus in laoreet purus, id fermentum velit. Phasellus eleifend libero in velit feugiat rhoncus.</p>
|
|
34
|
+
|
|
35
|
+
<p>Maecenas et mattis erat. Fusce volutpat urna tortor, sed lobortis sapien dignissim nec. Morbi id aliquet nunc. In hac habitasse platea dictumst. Mauris in lobortis dolor. Nunc orci urna, vehicula et turpis a, sagittis gravida est. Sed quis sem sapien. Morbi finibus neque lorem, non lobortis magna sodales vitae. In feugiat metus purus, ac rhoncus lorem tristique id. Nulla volutpat ex ac nulla ultrices, nec pellentesque urna semper. Donec a neque finibus, faucibus mi ac, viverra diam. Nullam a nisi vel enim gravida semper. Nam a ante nisi. Fusce tempor odio diam, vel commodo odio tempor et. Praesent sagittis consequat leo, eu efficitur justo finibus dignissim. Suspendisse in commodo sapien.</p>
|
|
36
|
+
|
|
37
|
+
<p>Pellentesque varius dui mauris, eget commodo lectus aliquet quis. Nulla vehicula vitae est ut euismod. Vivamus tempus tempus volutpat. Etiam cursus in leo et pulvinar. Vestibulum interdum porttitor efficitur. Morbi vestibulum dignissim massa nec molestie. Nullam eu sodales leo, nec pharetra arcu. Proin accumsan ligula ac lorem ullamcorper lacinia. Duis ut mi quis augue auctor tempor. Duis volutpat elit nec venenatis pharetra. Suspendisse luctus placerat tincidunt. Cras interdum interdum felis id vehicula. Aenean quis erat nec urna gravida tristique id in turpis. Integer dapibus placerat enim, vitae vehicula elit.</p>
|
|
38
|
+
|
|
39
|
+
<p>Fusce lorem magna, molestie euismod tincidunt et, hendrerit nec nibh. Duis id nibh nisl. Cras lectus sapien, hendrerit vitae dui eu, tincidunt tristique magna. Pellentesque molestie in velit in volutpat. Morbi egestas lacus neque, sit amet pulvinar sem volutpat ut. Aenean congue bibendum nisl ut malesuada. Donec mattis laoreet commodo. Nulla accumsan maximus porttitor.</p>
|
|
40
|
+
|
|
41
|
+
<p>Phasellus vel mi nec sapien pharetra mattis quis nec orci. Praesent eu suscipit nisi, a euismod nisi. Sed id dolor quis nisl tincidunt accumsan. Aliquam aliquam sed erat eu vehicula. Sed vestibulum felis egestas, pharetra nisi vel, fringilla sem. In maximus dui in hendrerit vulputate. Quisque lobortis iaculis lacus, sit amet lacinia sapien interdum sed. Vivamus congue neque fringilla tellus pellentesque blandit. Sed nec sapien at nisl molestie sollicitudin. Etiam a nulla tortor. Ut non tellus turpis. Curabitur ultrices pharetra velit, sed ultricies magna viverra sed.</p>
|
|
42
|
+
|
|
43
|
+
<p>Nunc in vehicula diam, ac molestie est. Aliquam efficitur at neque nec tristique. Nullam elementum arcu quis dolor sodales pulvinar. Phasellus pretium, ex at hendrerit rhoncus, orci diam mattis purus, a tempor nisi mauris vitae lectus. Aenean eget vulputate diam. Ut ultrices, nunc id hendrerit mattis, mi felis sagittis sapien, eget euismod massa risus eu risus. Morbi euismod vulputate ultrices. Etiam et pretium elit. Suspendisse potenti. Proin scelerisque convallis purus, at viverra massa fermentum a. Curabitur nunc orci, accumsan sodales massa a, dignissim luctus lorem. Praesent tincidunt augue eget tempus auctor.</p>
|
|
44
|
+
|
|
45
|
+
<p>Nam rhoncus tincidunt lorem, ac vehicula mauris luctus sit amet. Donec bibendum semper ex, quis condimentum eros elementum id. Morbi id commodo libero, id euismod ipsum. Nullam ante tellus, dapibus ut accumsan ac, pellentesque ut turpis. Quisque ligula nisi, pulvinar id urna id, aliquam laoreet enim. Fusce nec lorem malesuada, dapibus ipsum vel, rhoncus odio. Cras risus tortor, laoreet at odio ac, eleifend rutrum sapien. Sed scelerisque, urna ac iaculis condimentum, odio elit consequat leo, vitae ultricies mi felis eu nibh. Fusce condimentum id diam quis bibendum. Aliquam eget auctor nisi. Suspendisse eleifend sit amet libero at gravida. Cras libero enim, vulputate a nunc non, vulputate bibendum tellus.</p>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<style>
|
|
49
|
+
form {
|
|
50
|
+
background-color: white;
|
|
51
|
+
padding: 10px;
|
|
52
|
+
border-radius: 5px;
|
|
53
|
+
}
|
|
54
|
+
.content {
|
|
55
|
+
font-size: 18px;
|
|
56
|
+
line-height: 2;
|
|
57
|
+
}
|
|
58
|
+
</style>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { MultiSelect } from '$lib'
|
|
3
|
+
import { sleep } from 'txstate-utils'
|
|
4
|
+
let firstid
|
|
5
|
+
let secondid
|
|
6
|
+
function getOptionsFirst (val: string) {
|
|
7
|
+
return ['apple', 'orange', 'banana'].filter(o => o.includes(val)).map(value => ({ value }))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const secondItems = [
|
|
11
|
+
{ value: 'apple', label: 'Apple' },
|
|
12
|
+
{ value: 'orange', label: 'Orange' },
|
|
13
|
+
{ value: 'banana', label: 'Banana' }
|
|
14
|
+
]
|
|
15
|
+
async function getOptionsSecond (val: string) {
|
|
16
|
+
if (!val) return secondItems
|
|
17
|
+
await sleep(400)
|
|
18
|
+
return secondItems.filter(o => o.value.includes(val.toLocaleLowerCase()))
|
|
19
|
+
}
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<label for={firstid}>Fruit with only values</label><br>
|
|
23
|
+
<MultiSelect bind:id={firstid} name="test1" selected={[{ value: 'apple', label: 'apple' }]} getOptions={getOptionsFirst} />
|
|
24
|
+
|
|
25
|
+
<label for={secondid}>Fruit with value and label</label><br>
|
|
26
|
+
<MultiSelect bind:id={secondid} name="test2" selected={[{ value: 'orange', label: 'Orange' }, { value: 'banana', label: 'Banana' }]} placeholder='Choose some fruit' getOptions={getOptionsSecond} />
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { offset, OffsetStore } from '$lib'
|
|
3
|
+
export let debounce = false
|
|
4
|
+
const myoffset = new OffsetStore()
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<div class='resizable'>resize this div</div>
|
|
8
|
+
<div use:offset={{ store: myoffset, debounce }} class="offset">
|
|
9
|
+
This div's offset...<br>
|
|
10
|
+
Left: {$myoffset.left}<br>
|
|
11
|
+
Top: {$myoffset.top}<br>
|
|
12
|
+
Right: {$myoffset.right}<br>
|
|
13
|
+
Bottom: {$myoffset.bottom}
|
|
14
|
+
</div>
|
|
15
|
+
debounce: {debounce}
|
|
16
|
+
|
|
17
|
+
<style>
|
|
18
|
+
div {
|
|
19
|
+
max-width: 300px;
|
|
20
|
+
}
|
|
21
|
+
.resizable {
|
|
22
|
+
resize: both;
|
|
23
|
+
overflow: auto;
|
|
24
|
+
min-height: 100px;
|
|
25
|
+
background: lightblue;
|
|
26
|
+
border: 1px solid black;
|
|
27
|
+
}
|
|
28
|
+
.offset {
|
|
29
|
+
border: 1px solid blue;
|
|
30
|
+
text-align: center;
|
|
31
|
+
}
|
|
32
|
+
</style>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import PopupMenu from '$lib/components/PopupMenu.svelte'
|
|
3
|
+
let button: HTMLElement
|
|
4
|
+
|
|
5
|
+
export let items = [
|
|
6
|
+
{ value: '0', label: 'Sunday', disabled: true },
|
|
7
|
+
{ value: '1', label: 'Monday' },
|
|
8
|
+
{ value: '2', label: 'Tuesday' },
|
|
9
|
+
{ value: '3', label: 'Wednesday' },
|
|
10
|
+
{ value: '4', label: 'Thursday', disabled: true },
|
|
11
|
+
{ value: '5', label: 'Friday' },
|
|
12
|
+
{ value: '6', label: 'Saturday', disabled: true }
|
|
13
|
+
]
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<div class="topspacer"></div>
|
|
17
|
+
<div class="container">
|
|
18
|
+
<div class="container">
|
|
19
|
+
<button data-testid="button" class="ui gold button" bind:this={button}>Popup Menu</button>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="topspacer"></div>
|
|
22
|
+
<div class="container">
|
|
23
|
+
<PopupMenu buttonelement={button} {items}></PopupMenu>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<style>
|
|
28
|
+
.topspacer {
|
|
29
|
+
height: 20px;
|
|
30
|
+
}
|
|
31
|
+
.container {
|
|
32
|
+
position: relative;
|
|
33
|
+
}
|
|
34
|
+
</style>
|