@txstate-mws/svelte-components 1.1.0 → 1.1.1
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/actions/buttonify.d.ts +8 -0
- package/actions/buttonify.js +26 -0
- package/actions/glue.d.ts +16 -0
- package/actions/glue.js +82 -0
- package/actions/index.d.ts +7 -0
- package/actions/index.js +7 -0
- package/actions/offset.d.ts +15 -0
- package/actions/offset.js +35 -0
- package/actions/portal.d.ts +4 -0
- package/actions/portal.js +13 -0
- package/actions/resize.d.ts +20 -0
- package/actions/resize.js +57 -0
- package/actions/sticky.d.ts +33 -0
- package/actions/sticky.js +85 -0
- package/actions/stickyfixed.d.ts +25 -0
- package/actions/stickyfixed.js +157 -0
- package/components/Card.svelte +23 -0
- package/components/Card.svelte.d.ts +18 -0
- package/components/CardLayout.svelte +197 -0
- package/components/CardLayout.svelte.d.ts +21 -0
- package/{src/lib/components → components}/CollapsingTable.svelte +63 -65
- package/components/CollapsingTable.svelte.d.ts +59 -0
- package/{src/lib/components → components}/ConditionalWrapper.svelte +6 -7
- package/components/ConditionalWrapper.svelte.d.ts +23 -0
- package/components/FocusLock.svelte +107 -0
- package/components/FocusLock.svelte.d.ts +26 -0
- package/{src/lib/components → components}/Modal.svelte +22 -24
- package/components/Modal.svelte.d.ts +27 -0
- package/components/MultiSelect.svelte +179 -0
- package/components/MultiSelect.svelte.d.ts +35 -0
- package/components/PopupMenu.svelte +287 -0
- package/components/PopupMenu.svelte.d.ts +45 -0
- package/{src/lib/components → components}/ScreenReaderOnly.svelte +3 -4
- package/components/ScreenReaderOnly.svelte.d.ts +20 -0
- package/components/index.d.ts +9 -0
- package/components/index.js +9 -0
- package/index.d.ts +4 -0
- package/index.js +4 -0
- package/package.json +34 -13
- package/types/CardLayout.d.ts +14 -0
- package/types/CardLayout.js +1 -0
- package/types/CollapsingTable.d.ts +8 -0
- package/types/CollapsingTable.js +1 -0
- package/types/PopupMenu.d.ts +5 -0
- package/types/PopupMenu.js +1 -0
- package/types/index.d.ts +3 -0
- package/types/index.js +3 -0
- package/util/bodyoffset.d.ts +14 -0
- package/util/bodyoffset.js +72 -0
- package/util/index.d.ts +3 -0
- package/util/index.js +3 -0
- package/util/repositioned.d.ts +12 -0
- package/util/repositioned.js +81 -0
- package/util/util.d.ts +5 -0
- package/util/util.js +33 -0
- package/.eslintrc.cjs +0 -37
- package/docs/CollapsingTable.md +0 -79
- package/docs/ConditionalWrapper.md +0 -35
- package/docs/Modal.md +0 -63
- package/docs/PopupMenu.md +0 -31
- package/docs/ScreenReaderOnly.md +0 -10
- package/docs/actions.md +0 -84
- package/docs/stores.md +0 -203
- package/playwright.config.js +0 -112
- package/src/app.html +0 -14
- package/src/global.d.ts +0 -1
- package/src/lib/actions/buttonify.ts +0 -27
- package/src/lib/actions/glue.ts +0 -96
- package/src/lib/actions/index.ts +0 -7
- package/src/lib/actions/offset.ts +0 -43
- package/src/lib/actions/portal.ts +0 -13
- package/src/lib/actions/resize.ts +0 -72
- package/src/lib/actions/sticky.ts +0 -105
- package/src/lib/actions/stickyfixed.ts +0 -160
- package/src/lib/components/Card.svelte +0 -25
- package/src/lib/components/CardLayout.svelte +0 -188
- package/src/lib/components/FocusLock.svelte +0 -102
- package/src/lib/components/MultiSelect.svelte +0 -174
- package/src/lib/components/PopupMenu.svelte +0 -285
- package/src/lib/components/index.ts +0 -9
- package/src/lib/index.ts +0 -4
- package/src/lib/types/CardLayout.ts +0 -14
- package/src/lib/types/CollapsingTable.ts +0 -8
- package/src/lib/types/PopupMenu.ts +0 -5
- package/src/lib/types/index.ts +0 -3
- package/src/lib/util/bodyoffset.ts +0 -77
- package/src/lib/util/index.ts +0 -3
- package/src/lib/util/repositioned.ts +0 -91
- package/src/lib/util/util.ts +0 -36
- package/src/routes/__layout.svelte +0 -125
- package/src/routes/actions/index.svelte +0 -1
- package/src/routes/actions/offset.svelte +0 -4
- package/src/routes/actions/offsetdebounce.svelte +0 -4
- package/src/routes/actions/resize.svelte +0 -4
- package/src/routes/actions/resizedebounce.svelte +0 -4
- package/src/routes/actions/sticky.svelte +0 -4
- package/src/routes/cards/index.svelte +0 -4
- package/src/routes/cards/preserve.svelte +0 -4
- package/src/routes/collapsingtable/index.svelte +0 -4
- package/src/routes/collapsingtable/stickyheader.svelte +0 -4
- package/src/routes/index.svelte +0 -2
- package/src/routes/modal.svelte +0 -4
- package/src/routes/multiselect.svelte +0 -4
- package/src/routes/popupmenu/body.svelte +0 -4
- package/src/routes/popupmenu/complex.svelte +0 -4
- package/src/routes/popupmenu/empty.svelte +0 -5
- package/src/routes/popupmenu/index.svelte +0 -4
- package/src/routes/popupmenu/parent.svelte +0 -4
- package/src/routes/popupmenu/scrollable.svelte +0 -27
- package/src/routes/popupmenu/showselected.svelte +0 -4
- package/src/test/Cards.svelte +0 -64
- package/src/test/CollapsingTableTest.svelte +0 -36
- package/src/test/ModalTest.svelte +0 -58
- package/src/test/MultiSelectTest.svelte +0 -26
- package/src/test/Offset.svelte +0 -32
- package/src/test/PopupMenuBody.svelte +0 -34
- package/src/test/PopupMenuButtonIsParent.svelte +0 -36
- package/src/test/PopupMenuComplex.svelte +0 -34
- package/src/test/PopupMenuSimple.svelte +0 -36
- package/src/test/Resize.svelte +0 -29
- package/src/test/Sticky.svelte +0 -65
- package/static/layout.css +0 -263
- package/svelte.config.js +0 -15
- package/tests/.eslintrc.cjs +0 -19
- package/tests/CardLayout.test.js +0 -41
- package/tests/CollapsingTable.test.js +0 -37
- package/tests/Modal.test.js +0 -18
- package/tests/MultiSelect.test.js +0 -58
- package/tests/PopupMenu.test.js +0 -55
- package/tests/common.js +0 -4
- package/tsconfig.json +0 -31
package/docs/actions.md
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
# Actions
|
|
2
|
-
|
|
3
|
-
## use:buttonify
|
|
4
|
-
This action turns any HTML element into an accessible button that responds to keyboard events and has
|
|
5
|
-
role="button" and the tabindex attribute set. You are still responsible for making sure it has
|
|
6
|
-
readable content or a good label. Does NOT change styling at all.
|
|
7
|
-
```svelte
|
|
8
|
-
<div use:buttonify on:click={onClick}>You can tab to me and click me with the Enter key</div>
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## use:glue
|
|
12
|
-
This action glues the element it is placed on to the target specified, with a specified alignment
|
|
13
|
-
option. It is most useful for dropdown or autocomplete menus.
|
|
14
|
-
|
|
15
|
-
The element should have `position: absolute` on it already. It is not moved to a new place in the DOM,
|
|
16
|
-
it just gets `top`/`left`/`bottom`/`right` properties set.
|
|
17
|
-
|
|
18
|
-
If you have problems with the glued element being clipped by a parent with `overflow: hidden`, you
|
|
19
|
-
may combine it with the `use:portal` action to move it somewhere else; `use:glue` will recalculate
|
|
20
|
-
for the new location.
|
|
21
|
-
|
|
22
|
-
If you do not specify an `align` setting, the default is `auto`, which will evaluate the current
|
|
23
|
-
viewport and determine which alignment gives the glued element the most space to grow.
|
|
24
|
-
```svelte
|
|
25
|
-
<button bind:this={menubutton}>Activate Menu</button>
|
|
26
|
-
<ul use:glue={{ target: menubutton, align: 'bottomright' }}>
|
|
27
|
-
<li>Menu Item 1</li>
|
|
28
|
-
<li>Menu Item 2</li>
|
|
29
|
-
</ul>
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## use:offset
|
|
33
|
-
This action allows you to observe any DOM element's position in the body. You pass in a svelte store
|
|
34
|
-
and it will be updated when the element moves.
|
|
35
|
-
|
|
36
|
-
Another configuration option is `debounce: number`, which will debounce the updates for the specified
|
|
37
|
-
number of milliseconds. Use this option if you will make a complicated calculation after each update and do not
|
|
38
|
-
want to perform the calculation multiple times during a single animation or while the user is resizing
|
|
39
|
-
their viewport/window. It will defer the updates until the animation or resizing stops for at least `debounce`
|
|
40
|
-
milliseconds.
|
|
41
|
-
```svelte
|
|
42
|
-
<script>
|
|
43
|
-
const offset = writable({ top: 0, left: 0, bottom: 0, right: 0 })
|
|
44
|
-
</script>
|
|
45
|
-
<div use:offset={{ store: offset }}>distance between me and body top is {$offset.top}</div>
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## use:portal
|
|
49
|
-
This action allows you to move an element to a new place in the DOM upon mount. It's useful when your component
|
|
50
|
-
is sensitive to getting clipped by parents with overflow: hidden or for anything like a modal or toast message
|
|
51
|
-
that needs to be positioned relative to the body, but created within a smaller component, like a web form.
|
|
52
|
-
|
|
53
|
-
For convenience, the default is to move it to `document.body` when you `use:portal` without any config:
|
|
54
|
-
```svelte
|
|
55
|
-
<div use:portal>I'm in the body now!</div>
|
|
56
|
-
```
|
|
57
|
-
If you are accepting an optional target as a prop, this may cause unexpected behavior where your parent component
|
|
58
|
-
declines to pass a portal target, but your element ends up in `document.body`. To prevent this, you may pass `null`
|
|
59
|
-
instead of `undefined`, and the element will stay where it was before the change to `null`:
|
|
60
|
-
```svelte
|
|
61
|
-
<script>
|
|
62
|
-
export let portaltarget = undefined
|
|
63
|
-
</script>
|
|
64
|
-
<div use:portal={portaltarget || null}></div>
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## use:resize
|
|
68
|
-
This action allows you to observe an element's width and height with the `ResizeObserver`. Svelte has a built-in
|
|
69
|
-
`bind:clientWidth` functionality, but it works by inserting an invisible iframe into the DOM for each observation,
|
|
70
|
-
which is a little ugly and potentially slow.
|
|
71
|
-
|
|
72
|
-
You pass in a svelte store and it will be updated when the element's size changes.
|
|
73
|
-
|
|
74
|
-
Another configuration option is `debounce: number`, which will debounce the updates for the specified
|
|
75
|
-
number of milliseconds. Use this option if you will make a complicated calculation after each update and do not
|
|
76
|
-
want to perform the calculation multiple times during a single animation or while the user is resizing
|
|
77
|
-
their viewport/window. It will defer the updates until the animation or resizing stops for at least `debounce`
|
|
78
|
-
milliseconds.
|
|
79
|
-
```svelte
|
|
80
|
-
<script>
|
|
81
|
-
const size = writable({ clientWidth: 100, clientHeight: 100, offsetWidth: 100, offsetHeight: 100 })
|
|
82
|
-
</script>
|
|
83
|
-
<div use:offset={{ store: size }}>My dimensions are {$size.offsetWidth}x{$size.offsetHeight}</div>
|
|
84
|
-
```
|
package/docs/stores.md
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
# Overview
|
|
2
|
-
A set of stores to complement svelte stores. These stores perform deep equality checks on the data before updating subscribers, and are easy to subclass and add mutation methods.
|
|
3
|
-
|
|
4
|
-
## Subclassing
|
|
5
|
-
The most effective way to use a store is to create a Store subclass with mutation
|
|
6
|
-
methods. If you're familiar with redux, these methods would be similar to actions on
|
|
7
|
-
reducers.
|
|
8
|
-
```typescript
|
|
9
|
-
interface MyState {
|
|
10
|
-
foo: string
|
|
11
|
-
more: string
|
|
12
|
-
}
|
|
13
|
-
class MyStore extends Store<MyState> {
|
|
14
|
-
updateFoo (newfoo: string) {
|
|
15
|
-
// do some business logic here
|
|
16
|
-
newfoo = newfoo.trim().toLocaleLowerCase()
|
|
17
|
-
this.set({ ...this.value, foo: newfoo })
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
const store = new MyStore({ foo: 'bar', more: 'state' })
|
|
21
|
-
store.updateFoo('baz')
|
|
22
|
-
```
|
|
23
|
-
This way your store controls all the business logic and your components can be truly
|
|
24
|
-
reactive.
|
|
25
|
-
|
|
26
|
-
Note that `this.value` contains the current version of the store. You can also use the
|
|
27
|
-
`store.update` method to have the current value passed to you.
|
|
28
|
-
|
|
29
|
-
### Asynchronous mutations
|
|
30
|
-
Mutation methods become even more powerful when you do asynchronous updates. All of
|
|
31
|
-
your asynchronous code can be encapsulated in a single method, making it much easier
|
|
32
|
-
to think about how you want to handle race conditions and errors. Redux toolkit would
|
|
33
|
-
call these `thunk` functions, but here they are just another method.
|
|
34
|
-
```typescript
|
|
35
|
-
interface BookState {
|
|
36
|
-
loading?: boolean
|
|
37
|
-
book?: Book
|
|
38
|
-
error?: string
|
|
39
|
-
}
|
|
40
|
-
class BookStore extends Store<BookState> {
|
|
41
|
-
async getBook (id: number) {
|
|
42
|
-
if (this.value.loading) return // one book at a time!
|
|
43
|
-
this.set({ loading: true, book: undefined, error: undefined })
|
|
44
|
-
try {
|
|
45
|
-
const book = await api.getBook(id)
|
|
46
|
-
this.set({ loading: false, book, error: undefined })
|
|
47
|
-
} catch (e) {
|
|
48
|
-
this.set({ loading: false, book: undefined, error: e.message })
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Creating and sharing store instances
|
|
55
|
-
It's up to you to determine how you'd like to create and share stores. The singleton
|
|
56
|
-
pattern works great for end user applications. For instance, a global error store can
|
|
57
|
-
help individual components communicate with the layout component when they experience
|
|
58
|
-
a problem that's fatal for the page. Creating a singleton store for each screen in your
|
|
59
|
-
application is another powerful pattern and helps all your components share the screen
|
|
60
|
-
state.
|
|
61
|
-
|
|
62
|
-
Context can be very convenient as well. The main benefit of putting a store in Context
|
|
63
|
-
is it can be garbage collected when the context owner is unmounted. In SSR scenarios,
|
|
64
|
-
global stores can leak between clients unless you're careful to reset them on each request,
|
|
65
|
-
so storing in Context can be safer.
|
|
66
|
-
|
|
67
|
-
Similarly, a store can be created in a component and passed down as a prop, enabling
|
|
68
|
-
efficient two-way communication with its children. The main advantage over binding a
|
|
69
|
-
property is that the components can share the mutation methods.
|
|
70
|
-
|
|
71
|
-
## Store Types
|
|
72
|
-
|
|
73
|
-
### Store
|
|
74
|
-
This is the basic store type and performs deep equality checks before notifying subscribers.
|
|
75
|
-
It is intended to contain simple JSON state that is easy to programmatically explore. By
|
|
76
|
-
default, you are expected to update the store with fresh or cloned objects:
|
|
77
|
-
```typescript
|
|
78
|
-
const initialState = { foo: 'bar', more: 'state' }
|
|
79
|
-
const store = new Store(initialState)
|
|
80
|
-
store.update(state => ({ ...state, foo: 'baz' }))
|
|
81
|
-
```
|
|
82
|
-
### SafeStore
|
|
83
|
-
If it's not convenient to clone your objects, you can use a `SafeStore` instead,
|
|
84
|
-
which will allow you to mutate your state and then call `set` on it:
|
|
85
|
-
```typescript
|
|
86
|
-
const initialState = { foo: 'bar', more: 'state' }
|
|
87
|
-
const store = new SafeStore(initialState)
|
|
88
|
-
initialState.foo = 'baz'
|
|
89
|
-
store.set(initialState) // with a regular Store, this would not notify subscribers
|
|
90
|
-
```
|
|
91
|
-
This is only designed to work on pure JSON (objects and arrays, not Maps, Sets, or
|
|
92
|
-
any other data types). There is a slight performance cost due to deep cloning.
|
|
93
|
-
|
|
94
|
-
### ActiveStore
|
|
95
|
-
This is a store that does no equality checks before notifying subscribers. It can save a couple
|
|
96
|
-
CPU cycles by skipping the deep equality check, but may be an overall performance degradation
|
|
97
|
-
if subscribers are doing significant work and there are many updates that do not change the state.
|
|
98
|
-
It is also vulnerable to unexpected infinite loops if subscribers end up triggering a redundant
|
|
99
|
-
`set` command. This store is safe to use with mutated objects and is the only one that can
|
|
100
|
-
handle non-JSON objects in the state.
|
|
101
|
-
|
|
102
|
-
### SubStore
|
|
103
|
-
This is a store that makes it convenient to interact with a portion of a larger store. You
|
|
104
|
-
subscribe to a parent store in the constructor, and when you update the SubStore, your
|
|
105
|
-
changes will be sent to the parent store.
|
|
106
|
-
|
|
107
|
-
#### Getter / Setter version of SubStore
|
|
108
|
-
This requires both a getter and setter function to handle each direction. The setter is given
|
|
109
|
-
the new SubStore state and the existing parent store state and should return a new state for
|
|
110
|
-
the parent store.
|
|
111
|
-
```typescript
|
|
112
|
-
const parentStore = new Store({
|
|
113
|
-
about: { header: 'About' },
|
|
114
|
-
events: { header: 'Events' }
|
|
115
|
-
})
|
|
116
|
-
const aboutStore = new SubStore(parentStore, pState => pState.about, (newAboutState, pState) => ({ ...pState, about: newAboutState }))
|
|
117
|
-
aboutStore.set({ header: 'About Us' })
|
|
118
|
-
// aboutStore state is { header: 'About Us' }
|
|
119
|
-
// parentStore state is { about: { header: 'About Us' }, events: { header: 'Events' } }
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
#### Accessor string version of SubStore
|
|
123
|
-
Alternatively, a dot-prop accessor string can be provided and both the getter and setter will be
|
|
124
|
-
automatically generated.
|
|
125
|
-
```typescript
|
|
126
|
-
const parentStore = new Store({
|
|
127
|
-
about: { header: 'About' },
|
|
128
|
-
events: { header: 'Events' }
|
|
129
|
-
})
|
|
130
|
-
const aboutStore = subStore(parentStore, 'about')
|
|
131
|
-
aboutStore.set({ header: 'About Us' })
|
|
132
|
-
// aboutStore state is { header: 'About Us' }
|
|
133
|
-
// parentStore state is { about: { header: 'About Us' }, events: { header: 'Events' } }
|
|
134
|
-
```
|
|
135
|
-
Note the use of a `subStore` function rather than `new SubStore`. `new SubStore` will work just
|
|
136
|
-
fine, but since typescript (at the time of writing) does not allow method overloads on the
|
|
137
|
-
constructor, there was no way to automatically derive types. Make a habit of using the `subStore`
|
|
138
|
-
function if you use typescript.
|
|
139
|
-
|
|
140
|
-
#### Subclassing SubStore
|
|
141
|
-
If you want to create a class and mutation methods for your SubStore, you'll want to create an
|
|
142
|
-
interface for your parent type that requires a minimum interface of the part you deal with. Then
|
|
143
|
-
you create a custom constructor with your accessor or setter/getter.
|
|
144
|
-
```typescript
|
|
145
|
-
interface AboutType {
|
|
146
|
-
header: string
|
|
147
|
-
}
|
|
148
|
-
interface HasAboutType {
|
|
149
|
-
about: AboutType
|
|
150
|
-
}
|
|
151
|
-
class AboutStore<ParentType extends HasAboutType> extends SubStore<AboutType, ParentType> {
|
|
152
|
-
constructor (parentStore: WritableSubject<ParentType>) {
|
|
153
|
-
super(parentStore, 'about')
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
updateHeader (newHeader: string) {
|
|
157
|
-
this.update(state => ({ ...state, header: newHeader }))
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
const parentStore = new Store({
|
|
161
|
-
about: { header: 'About' },
|
|
162
|
-
events: { header: 'Events' }
|
|
163
|
-
})
|
|
164
|
-
const aboutStore = new AboutStore(parentStore)
|
|
165
|
-
aboutStore.updateHeader('About Us')
|
|
166
|
-
```
|
|
167
|
-
Note the use of `WritableSubject` in the constructor. This is exported from our library, and is
|
|
168
|
-
a generic way of accepting any kind of store - it accepts anything with a `subscribe` and `update`
|
|
169
|
-
method. So you can write a SubStore that accepts default svelte stores as well as one of our stores.
|
|
170
|
-
|
|
171
|
-
### derivedStore
|
|
172
|
-
`derivedStore` is designed to allow you to compile data from one or more parent stores into another
|
|
173
|
-
read-only store. Each time any parent store changes, we'll run a function to derive a new state from
|
|
174
|
-
the parent state(s). This is extremely similar to svelte's `derived` store, but subscribers will only
|
|
175
|
-
be notified if the derived state has changed (uses a deep-equal instead of svelte's shallow equality).
|
|
176
|
-
```typescript
|
|
177
|
-
const num1Store = new Store(5)
|
|
178
|
-
const num2Store = new Store(7)
|
|
179
|
-
const sumStore = derivedStore([num1Store, num2Store], ([num1, num2]) => num1 + num2)
|
|
180
|
-
// sumStore state is 12
|
|
181
|
-
num1Store.set(3)
|
|
182
|
-
// sumStore state is 10
|
|
183
|
-
```
|
|
184
|
-
If you only use one parent store, it's possible to use a dot-prop string as the getter, basically a
|
|
185
|
-
read-only version of SubStore.
|
|
186
|
-
|
|
187
|
-
### convertStore
|
|
188
|
-
`convertStore` is provided to add deep equality checks to a svelte store. The state is duplicated into
|
|
189
|
-
one of our stores, but the duplicate is kept in sync with the parent svelte store and vice-versa. You can
|
|
190
|
-
feel free to `set` and `update` the converted store even if some of your code still uses the parent svelte
|
|
191
|
-
store.
|
|
192
|
-
```typescript
|
|
193
|
-
const svelteStore = writable({ putit: 'there' })
|
|
194
|
-
const deepEqualStore = convertStore(svelteStore)
|
|
195
|
-
svelteStore.set({ putit: 'here' })
|
|
196
|
-
// both stores have state { putit: 'here' }
|
|
197
|
-
deepEqualStore.set({ putit: 'there' })
|
|
198
|
-
// both stores are back to { putit: 'there' }
|
|
199
|
-
deepEqualStore.set({ putit: 'there' })
|
|
200
|
-
// no subscribers are notified, because deepEqualStore caught that nothing was updated
|
|
201
|
-
svelteStore.set({ putit: 'there' })
|
|
202
|
-
// svelteStore subscribers are notified, but deepEqualStore subscribers are not
|
|
203
|
-
```
|
package/playwright.config.js
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { devices } from '@playwright/test';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* See https://playwright.dev/docs/test-configuration.
|
|
5
|
-
*/
|
|
6
|
-
export const config = {
|
|
7
|
-
|
|
8
|
-
testDir: './tests',
|
|
9
|
-
|
|
10
|
-
/* Maximum time one test can run for. */
|
|
11
|
-
timeout: 30 * 1000,
|
|
12
|
-
|
|
13
|
-
expect: {
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Maximum time expect() should wait for the condition to be met.
|
|
17
|
-
* For example in `await expect(locator).toHaveText();`
|
|
18
|
-
*/
|
|
19
|
-
timeout: 5000
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
|
23
|
-
forbidOnly: !!process.env.CI,
|
|
24
|
-
|
|
25
|
-
/* Retry on CI only */
|
|
26
|
-
retries: process.env.CI ? 2 : 0,
|
|
27
|
-
|
|
28
|
-
/* Opt out of parallel tests on CI. */
|
|
29
|
-
workers: process.env.CI ? 1 : undefined,
|
|
30
|
-
|
|
31
|
-
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
|
32
|
-
reporter: 'line',
|
|
33
|
-
|
|
34
|
-
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
|
35
|
-
use: {
|
|
36
|
-
|
|
37
|
-
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
|
38
|
-
actionTimeout: 0,
|
|
39
|
-
|
|
40
|
-
/* Base URL to use in actions like `await page.goto('/')`. */
|
|
41
|
-
// baseURL: 'http://localhost:3000',
|
|
42
|
-
|
|
43
|
-
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
|
44
|
-
trace: 'on-first-retry',
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
/* Configure projects for major browsers */
|
|
48
|
-
projects: [
|
|
49
|
-
{
|
|
50
|
-
name: 'chromium',
|
|
51
|
-
|
|
52
|
-
/* Project-specific settings. */
|
|
53
|
-
use: {
|
|
54
|
-
...devices['Desktop Chrome'],
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
|
|
58
|
-
{
|
|
59
|
-
name: 'firefox',
|
|
60
|
-
use: {
|
|
61
|
-
...devices['Desktop Firefox'],
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
{
|
|
66
|
-
name: 'webkit',
|
|
67
|
-
use: {
|
|
68
|
-
...devices['Desktop Safari'],
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
/* Test against mobile viewports. */
|
|
73
|
-
// {
|
|
74
|
-
// name: 'Mobile Chrome',
|
|
75
|
-
// use: {
|
|
76
|
-
// ...devices['Pixel 5'],
|
|
77
|
-
// },
|
|
78
|
-
// },
|
|
79
|
-
// {
|
|
80
|
-
// name: 'Mobile Safari',
|
|
81
|
-
// use: {
|
|
82
|
-
// ...devices['iPhone 12'],
|
|
83
|
-
// },
|
|
84
|
-
// },
|
|
85
|
-
|
|
86
|
-
/* Test against branded browsers. */
|
|
87
|
-
// {
|
|
88
|
-
// name: 'Microsoft Edge',
|
|
89
|
-
// use: {
|
|
90
|
-
// channel: 'msedge',
|
|
91
|
-
// },
|
|
92
|
-
// },
|
|
93
|
-
// {
|
|
94
|
-
// name: 'Google Chrome',
|
|
95
|
-
// use: {
|
|
96
|
-
// channel: 'chrome',
|
|
97
|
-
// },
|
|
98
|
-
// },
|
|
99
|
-
],
|
|
100
|
-
|
|
101
|
-
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
|
|
102
|
-
// outputDir: 'test-results/',
|
|
103
|
-
|
|
104
|
-
/* Run your local dev server before starting the tests */
|
|
105
|
-
webServer: {
|
|
106
|
-
command: 'npm run build && npm run preview',
|
|
107
|
-
port: 3000,
|
|
108
|
-
timeout: 120 * 1000,
|
|
109
|
-
reuseExistingServer: !process.env.CI
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
export default config;
|
package/src/app.html
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="description" content="" />
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
-
<link rel="stylesheet" href="https://unpkg.com/purecss@2.0.6/build/pure-min.css" integrity="sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5" crossorigin="anonymous">
|
|
8
|
-
<link rel="stylesheet" href="/layout.css" />
|
|
9
|
-
%svelte.head%
|
|
10
|
-
</head>
|
|
11
|
-
<body>
|
|
12
|
-
<div id="svelte">%svelte.body%</div>
|
|
13
|
-
</body>
|
|
14
|
-
</html>
|
package/src/global.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/// <reference types="@sveltejs/kit" />
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Turns any HTML element into an accessible button that responds to keyboard events and has
|
|
3
|
-
* role="button" and the tabindex attribute set. You are still responsible for making sure it has
|
|
4
|
-
* readable content or a good label.
|
|
5
|
-
*/
|
|
6
|
-
export function buttonify (node: HTMLElement) {
|
|
7
|
-
function keydown (e: KeyboardEvent) {
|
|
8
|
-
if (e.key === 'Enter') {
|
|
9
|
-
if (!e.shiftKey && !e.metaKey && !e.ctrlKey && !e.altKey) {
|
|
10
|
-
e.preventDefault()
|
|
11
|
-
e.stopPropagation()
|
|
12
|
-
node.dispatchEvent(new CustomEvent('click'))
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
node.addEventListener('keydown', keydown)
|
|
18
|
-
if (node.tabIndex < 0) node.tabIndex = 0
|
|
19
|
-
node.setAttribute('role', 'button')
|
|
20
|
-
node.style.cursor = 'pointer'
|
|
21
|
-
|
|
22
|
-
return {
|
|
23
|
-
destroy () {
|
|
24
|
-
node.removeEventListener('keydown', keydown)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
package/src/lib/actions/glue.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import type { SettableSubject } from '@txstate-mws/svelte-store'
|
|
2
|
-
import { sharedOffsetParent, targetOffset, watchForPositionChangeInContainer } from '../util/index.js'
|
|
3
|
-
import type { ElementOffsets } from '../util/index.js'
|
|
4
|
-
export type GlueAlignOpts = 'auto'|'bottomleft'|'bottomright'|'topleft'|'topright'
|
|
5
|
-
|
|
6
|
-
export interface GlueArgs<T extends GlueAlignStore = GlueAlignStore> {
|
|
7
|
-
target: HTMLElement
|
|
8
|
-
align?: GlueAlignOpts
|
|
9
|
-
cover?: boolean
|
|
10
|
-
store?: SettableSubject<T>
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface GlueAlignStore {
|
|
14
|
-
valign: 'bottom'|'top'
|
|
15
|
-
halign: 'left'|'right'
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let setBodyRelative = false
|
|
19
|
-
export function glue (el: HTMLElement, { target, align = 'auto', cover = false, store }: GlueArgs) {
|
|
20
|
-
if (!setBodyRelative) {
|
|
21
|
-
setBodyRelative = true
|
|
22
|
-
document.body.style.position = 'relative'
|
|
23
|
-
}
|
|
24
|
-
let halign: GlueAlignStore['halign']
|
|
25
|
-
let valign: GlueAlignStore['valign']
|
|
26
|
-
let sharedOffset = sharedOffsetParent(el, target)
|
|
27
|
-
|
|
28
|
-
function reposition (offset: Required<ElementOffsets>) {
|
|
29
|
-
if (!target || !sharedOffset || !(el.offsetParent instanceof HTMLElement)) return
|
|
30
|
-
let autoalign = align
|
|
31
|
-
const parentoffset = el.offsetParent === sharedOffset && target !== sharedOffset ? { top: 0, left: 0, bottom: 0, right: 0 } : targetOffset(el.offsetParent, sharedOffset)
|
|
32
|
-
if (align === 'auto') {
|
|
33
|
-
const rect = target.getBoundingClientRect()
|
|
34
|
-
const leftright = window.innerWidth - rect.right > rect.left ? 'left' : 'right'
|
|
35
|
-
const topbottom = window.innerHeight - rect.bottom > rect.top ? 'bottom' : 'top'
|
|
36
|
-
autoalign = topbottom + leftright as 'auto'
|
|
37
|
-
}
|
|
38
|
-
const targetHeight = target.offsetHeight
|
|
39
|
-
if (autoalign === 'bottomleft') {
|
|
40
|
-
requestAnimationFrame(() => {
|
|
41
|
-
el.style.top = `${offset.top + (cover ? 0 : targetHeight) - parentoffset.top}px`
|
|
42
|
-
el.style.left = `${offset.left - parentoffset.left}px`
|
|
43
|
-
el.style.bottom = ''
|
|
44
|
-
el.style.right = ''
|
|
45
|
-
})
|
|
46
|
-
valign = 'bottom'
|
|
47
|
-
halign = 'left'
|
|
48
|
-
} else if (autoalign === 'bottomright') {
|
|
49
|
-
requestAnimationFrame(() => {
|
|
50
|
-
el.style.top = `${offset.top + (cover ? 0 : targetHeight) - parentoffset.top}px`
|
|
51
|
-
el.style.left = ''
|
|
52
|
-
el.style.bottom = ''
|
|
53
|
-
el.style.right = `${offset.right - parentoffset.right}px`
|
|
54
|
-
})
|
|
55
|
-
valign = 'bottom'
|
|
56
|
-
halign = 'right'
|
|
57
|
-
} else if (autoalign === 'topleft') {
|
|
58
|
-
requestAnimationFrame(() => {
|
|
59
|
-
el.style.top = ''
|
|
60
|
-
el.style.left = `${offset.left - parentoffset.left}px`
|
|
61
|
-
el.style.bottom = `${offset.bottom + (cover ? 0 : targetHeight) - parentoffset.bottom}px`
|
|
62
|
-
el.style.right = ''
|
|
63
|
-
})
|
|
64
|
-
valign = 'top'
|
|
65
|
-
halign = 'left'
|
|
66
|
-
} else if (autoalign === 'topright') {
|
|
67
|
-
requestAnimationFrame(() => {
|
|
68
|
-
el.style.top = ''
|
|
69
|
-
el.style.left = ''
|
|
70
|
-
el.style.bottom = `${offset.bottom + (cover ? 0 : targetHeight) - parentoffset.bottom}px`
|
|
71
|
-
el.style.right = `${offset.right - parentoffset.right}px`
|
|
72
|
-
})
|
|
73
|
-
valign = 'top'
|
|
74
|
-
halign = 'right'
|
|
75
|
-
}
|
|
76
|
-
store?.update(v => ({ ...v, valign, halign }))
|
|
77
|
-
}
|
|
78
|
-
const { destroy, update } = watchForPositionChangeInContainer(target, sharedOffset, reposition)
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
update ({ target: utarget, align: ualign = 'auto', cover: ucover = false, store: ustore }: GlueArgs) {
|
|
82
|
-
align = ualign
|
|
83
|
-
cover = ucover
|
|
84
|
-
if (ustore && ustore !== store) {
|
|
85
|
-
ustore.update(v => ({ ...v, valign, halign }))
|
|
86
|
-
store = ustore
|
|
87
|
-
}
|
|
88
|
-
if (target !== utarget) {
|
|
89
|
-
sharedOffset = sharedOffsetParent(el, utarget)
|
|
90
|
-
target = utarget
|
|
91
|
-
update(target, sharedOffset, reposition)
|
|
92
|
-
}
|
|
93
|
-
},
|
|
94
|
-
destroy
|
|
95
|
-
}
|
|
96
|
-
}
|
package/src/lib/actions/index.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { Store } from '@txstate-mws/svelte-store'
|
|
2
|
-
import type { WritableSubject } from '@txstate-mws/svelte-store'
|
|
3
|
-
import { debounced, watchForPositionChange } from '../util/index.js'
|
|
4
|
-
import type { ElementOffsets } from '../util/index.js'
|
|
5
|
-
|
|
6
|
-
interface OffsetConfig<T extends ElementOffsets = ElementOffsets> {
|
|
7
|
-
debounce?: boolean|number
|
|
8
|
-
store?: WritableSubject<T>
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export class OffsetStore extends Store<ElementOffsets> {
|
|
12
|
-
constructor (initialState?: ElementOffsets) {
|
|
13
|
-
super(initialState ?? { top: 0, right: 0, bottom: 0, left: 0 })
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function offset (el: HTMLElement, config?: OffsetConfig) {
|
|
18
|
-
if (config?.debounce === true) config.debounce = 100
|
|
19
|
-
let lastOffset: ElementOffsets = {}
|
|
20
|
-
function lookforoffsetchange (current: ElementOffsets) {
|
|
21
|
-
lastOffset = current
|
|
22
|
-
if (config?.store) config.store.update(v => ({ ...v, ...current }))
|
|
23
|
-
el.dispatchEvent(new CustomEvent('offset', { detail: current }))
|
|
24
|
-
}
|
|
25
|
-
let resolvedchange = config?.debounce ? debounced(lookforoffsetchange, config.debounce) : lookforoffsetchange
|
|
26
|
-
|
|
27
|
-
const { destroy, update } = watchForPositionChange(el, resolvedchange)
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
update (newConfig?: OffsetConfig) {
|
|
31
|
-
if (newConfig?.debounce === true) newConfig.debounce = 100
|
|
32
|
-
if (newConfig?.store !== config?.store) {
|
|
33
|
-
newConfig?.store?.update(v => ({ ...v, ...lastOffset }))
|
|
34
|
-
}
|
|
35
|
-
if (newConfig?.debounce !== config?.debounce) {
|
|
36
|
-
resolvedchange = newConfig?.debounce ? debounced(lookforoffsetchange, newConfig.debounce as any) : lookforoffsetchange
|
|
37
|
-
update(el, resolvedchange)
|
|
38
|
-
}
|
|
39
|
-
config = newConfig
|
|
40
|
-
},
|
|
41
|
-
destroy
|
|
42
|
-
}
|
|
43
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export function portal (el: HTMLElement, target: HTMLElement|null = document.body) {
|
|
2
|
-
function update (target: HTMLElement|null = document.body) {
|
|
3
|
-
target?.appendChild(el)
|
|
4
|
-
}
|
|
5
|
-
function destroy () {
|
|
6
|
-
el.parentElement?.removeChild(el)
|
|
7
|
-
}
|
|
8
|
-
update(target)
|
|
9
|
-
return {
|
|
10
|
-
update,
|
|
11
|
-
destroy
|
|
12
|
-
}
|
|
13
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { equal } from 'txstate-utils'
|
|
2
|
-
import { Store } from '@txstate-mws/svelte-store'
|
|
3
|
-
import type { SettableSubject } from '@txstate-mws/svelte-store'
|
|
4
|
-
import { debounced } from '../util/index.js'
|
|
5
|
-
|
|
6
|
-
export interface ElementSize {
|
|
7
|
-
clientWidth?: number
|
|
8
|
-
clientHeight?: number
|
|
9
|
-
offsetWidth?: number
|
|
10
|
-
offsetHeight?: number
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface ResizeConfig {
|
|
14
|
-
debounce?: boolean|number
|
|
15
|
-
store?: SettableSubject<ElementSize>
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export class ResizeStore extends Store<ElementSize> {
|
|
19
|
-
constructor (initialState?: ElementSize) {
|
|
20
|
-
super(initialState ?? typeof window === 'undefined'
|
|
21
|
-
? {
|
|
22
|
-
clientWidth: 1024,
|
|
23
|
-
clientHeight: 1024,
|
|
24
|
-
offsetWidth: 1024,
|
|
25
|
-
offsetHeight: 1024
|
|
26
|
-
}
|
|
27
|
-
: {
|
|
28
|
-
clientWidth: window.innerWidth,
|
|
29
|
-
clientHeight: window.innerHeight,
|
|
30
|
-
offsetWidth: window.innerWidth,
|
|
31
|
-
offsetHeight: window.innerHeight
|
|
32
|
-
}
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function resize (el: HTMLElement, config?: ResizeConfig) {
|
|
38
|
-
if (config?.debounce === true) config.debounce = 100
|
|
39
|
-
let lastSize: ElementSize = {}
|
|
40
|
-
function watch () {
|
|
41
|
-
const current = { clientWidth: el.clientWidth, clientHeight: el.clientHeight, offsetWidth: el.offsetWidth, offsetHeight: el.offsetHeight }
|
|
42
|
-
if (!equal(lastSize, current)) {
|
|
43
|
-
lastSize = current
|
|
44
|
-
if (config?.store) config.store.set(current)
|
|
45
|
-
el.dispatchEvent(new CustomEvent('resize', { detail: current }))
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (typeof ResizeObserver === 'undefined') return watch() // support SSR
|
|
50
|
-
|
|
51
|
-
let observer = new ResizeObserver(config?.debounce ? debounced(watch, config.debounce) : watch)
|
|
52
|
-
observer.observe(el)
|
|
53
|
-
watch()
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
update (newConfig?: ResizeConfig) {
|
|
57
|
-
if (newConfig?.debounce === true) newConfig.debounce = 100
|
|
58
|
-
if (newConfig?.store !== config?.store) {
|
|
59
|
-
newConfig?.store?.set(lastSize)
|
|
60
|
-
}
|
|
61
|
-
if (!!newConfig?.debounce !== !!config?.debounce) {
|
|
62
|
-
observer.disconnect()
|
|
63
|
-
observer = new ResizeObserver(newConfig?.debounce ? debounced(watch, newConfig.debounce) : watch)
|
|
64
|
-
observer.observe(el)
|
|
65
|
-
}
|
|
66
|
-
config = newConfig
|
|
67
|
-
},
|
|
68
|
-
destroy () {
|
|
69
|
-
observer.disconnect()
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|