@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.
Files changed (131) hide show
  1. package/actions/buttonify.d.ts +8 -0
  2. package/actions/buttonify.js +26 -0
  3. package/actions/glue.d.ts +16 -0
  4. package/actions/glue.js +82 -0
  5. package/actions/index.d.ts +7 -0
  6. package/actions/index.js +7 -0
  7. package/actions/offset.d.ts +15 -0
  8. package/actions/offset.js +35 -0
  9. package/actions/portal.d.ts +4 -0
  10. package/actions/portal.js +13 -0
  11. package/actions/resize.d.ts +20 -0
  12. package/actions/resize.js +57 -0
  13. package/actions/sticky.d.ts +33 -0
  14. package/actions/sticky.js +85 -0
  15. package/actions/stickyfixed.d.ts +25 -0
  16. package/actions/stickyfixed.js +157 -0
  17. package/components/Card.svelte +23 -0
  18. package/components/Card.svelte.d.ts +18 -0
  19. package/components/CardLayout.svelte +197 -0
  20. package/components/CardLayout.svelte.d.ts +21 -0
  21. package/{src/lib/components → components}/CollapsingTable.svelte +63 -65
  22. package/components/CollapsingTable.svelte.d.ts +59 -0
  23. package/{src/lib/components → components}/ConditionalWrapper.svelte +6 -7
  24. package/components/ConditionalWrapper.svelte.d.ts +23 -0
  25. package/components/FocusLock.svelte +107 -0
  26. package/components/FocusLock.svelte.d.ts +26 -0
  27. package/{src/lib/components → components}/Modal.svelte +22 -24
  28. package/components/Modal.svelte.d.ts +27 -0
  29. package/components/MultiSelect.svelte +179 -0
  30. package/components/MultiSelect.svelte.d.ts +35 -0
  31. package/components/PopupMenu.svelte +287 -0
  32. package/components/PopupMenu.svelte.d.ts +45 -0
  33. package/{src/lib/components → components}/ScreenReaderOnly.svelte +3 -4
  34. package/components/ScreenReaderOnly.svelte.d.ts +20 -0
  35. package/components/index.d.ts +9 -0
  36. package/components/index.js +9 -0
  37. package/index.d.ts +4 -0
  38. package/index.js +4 -0
  39. package/package.json +34 -13
  40. package/types/CardLayout.d.ts +14 -0
  41. package/types/CardLayout.js +1 -0
  42. package/types/CollapsingTable.d.ts +8 -0
  43. package/types/CollapsingTable.js +1 -0
  44. package/types/PopupMenu.d.ts +5 -0
  45. package/types/PopupMenu.js +1 -0
  46. package/types/index.d.ts +3 -0
  47. package/types/index.js +3 -0
  48. package/util/bodyoffset.d.ts +14 -0
  49. package/util/bodyoffset.js +72 -0
  50. package/util/index.d.ts +3 -0
  51. package/util/index.js +3 -0
  52. package/util/repositioned.d.ts +12 -0
  53. package/util/repositioned.js +81 -0
  54. package/util/util.d.ts +5 -0
  55. package/util/util.js +33 -0
  56. package/.eslintrc.cjs +0 -37
  57. package/docs/CollapsingTable.md +0 -79
  58. package/docs/ConditionalWrapper.md +0 -35
  59. package/docs/Modal.md +0 -63
  60. package/docs/PopupMenu.md +0 -31
  61. package/docs/ScreenReaderOnly.md +0 -10
  62. package/docs/actions.md +0 -84
  63. package/docs/stores.md +0 -203
  64. package/playwright.config.js +0 -112
  65. package/src/app.html +0 -14
  66. package/src/global.d.ts +0 -1
  67. package/src/lib/actions/buttonify.ts +0 -27
  68. package/src/lib/actions/glue.ts +0 -96
  69. package/src/lib/actions/index.ts +0 -7
  70. package/src/lib/actions/offset.ts +0 -43
  71. package/src/lib/actions/portal.ts +0 -13
  72. package/src/lib/actions/resize.ts +0 -72
  73. package/src/lib/actions/sticky.ts +0 -105
  74. package/src/lib/actions/stickyfixed.ts +0 -160
  75. package/src/lib/components/Card.svelte +0 -25
  76. package/src/lib/components/CardLayout.svelte +0 -188
  77. package/src/lib/components/FocusLock.svelte +0 -102
  78. package/src/lib/components/MultiSelect.svelte +0 -174
  79. package/src/lib/components/PopupMenu.svelte +0 -285
  80. package/src/lib/components/index.ts +0 -9
  81. package/src/lib/index.ts +0 -4
  82. package/src/lib/types/CardLayout.ts +0 -14
  83. package/src/lib/types/CollapsingTable.ts +0 -8
  84. package/src/lib/types/PopupMenu.ts +0 -5
  85. package/src/lib/types/index.ts +0 -3
  86. package/src/lib/util/bodyoffset.ts +0 -77
  87. package/src/lib/util/index.ts +0 -3
  88. package/src/lib/util/repositioned.ts +0 -91
  89. package/src/lib/util/util.ts +0 -36
  90. package/src/routes/__layout.svelte +0 -125
  91. package/src/routes/actions/index.svelte +0 -1
  92. package/src/routes/actions/offset.svelte +0 -4
  93. package/src/routes/actions/offsetdebounce.svelte +0 -4
  94. package/src/routes/actions/resize.svelte +0 -4
  95. package/src/routes/actions/resizedebounce.svelte +0 -4
  96. package/src/routes/actions/sticky.svelte +0 -4
  97. package/src/routes/cards/index.svelte +0 -4
  98. package/src/routes/cards/preserve.svelte +0 -4
  99. package/src/routes/collapsingtable/index.svelte +0 -4
  100. package/src/routes/collapsingtable/stickyheader.svelte +0 -4
  101. package/src/routes/index.svelte +0 -2
  102. package/src/routes/modal.svelte +0 -4
  103. package/src/routes/multiselect.svelte +0 -4
  104. package/src/routes/popupmenu/body.svelte +0 -4
  105. package/src/routes/popupmenu/complex.svelte +0 -4
  106. package/src/routes/popupmenu/empty.svelte +0 -5
  107. package/src/routes/popupmenu/index.svelte +0 -4
  108. package/src/routes/popupmenu/parent.svelte +0 -4
  109. package/src/routes/popupmenu/scrollable.svelte +0 -27
  110. package/src/routes/popupmenu/showselected.svelte +0 -4
  111. package/src/test/Cards.svelte +0 -64
  112. package/src/test/CollapsingTableTest.svelte +0 -36
  113. package/src/test/ModalTest.svelte +0 -58
  114. package/src/test/MultiSelectTest.svelte +0 -26
  115. package/src/test/Offset.svelte +0 -32
  116. package/src/test/PopupMenuBody.svelte +0 -34
  117. package/src/test/PopupMenuButtonIsParent.svelte +0 -36
  118. package/src/test/PopupMenuComplex.svelte +0 -34
  119. package/src/test/PopupMenuSimple.svelte +0 -36
  120. package/src/test/Resize.svelte +0 -29
  121. package/src/test/Sticky.svelte +0 -65
  122. package/static/layout.css +0 -263
  123. package/svelte.config.js +0 -15
  124. package/tests/.eslintrc.cjs +0 -19
  125. package/tests/CardLayout.test.js +0 -41
  126. package/tests/CollapsingTable.test.js +0 -37
  127. package/tests/Modal.test.js +0 -18
  128. package/tests/MultiSelect.test.js +0 -58
  129. package/tests/PopupMenu.test.js +0 -55
  130. package/tests/common.js +0 -4
  131. 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
- ```
@@ -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
- }
@@ -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
- }
@@ -1,7 +0,0 @@
1
- export * from './buttonify.js'
2
- export * from './glue.js'
3
- export * from './offset.js'
4
- export * from './portal.js'
5
- export * from './resize.js'
6
- export * from './sticky.js'
7
- export * from './stickyfixed.js'
@@ -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
- }