@hotosm/ui 0.6.1 → 0.8.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.
Files changed (59) hide show
  1. package/README.md +234 -139
  2. package/dist/chunks/{chunk.GEAU3JJH.js → chunk.252GL6HT.js} +11 -11
  3. package/dist/chunks/{chunk.AL4CJN73.js → chunk.5QY2VLMG.js} +1 -1
  4. package/dist/chunks/chunk.6JUW2IZJ.js +105 -0
  5. package/dist/chunks/{chunk.DDWCUURX.js → chunk.BMD7HOGS.js} +141 -50
  6. package/dist/chunks/chunk.F26WEVPO.js +526 -0
  7. package/dist/chunks/chunk.FN6G7X23.js +251 -0
  8. package/dist/chunks/{chunk.OICCPCO3.js → chunk.IILWGAOM.js} +1 -1
  9. package/dist/chunks/{chunk.G6ZZSFVM.js → chunk.JYPMFVPO.js} +1 -1
  10. package/dist/chunks/chunk.LKMWWUYT.js +20 -0
  11. package/dist/chunks/{chunk.J7OUGJBS.js → chunk.LVC64FO7.js} +2 -2
  12. package/dist/chunks/{chunk.ARF626EG.js → chunk.LXN3PNAP.js} +1 -1
  13. package/dist/chunks/{chunk.7TYQYCSB.js → chunk.M3LK72JN.js} +1 -1
  14. package/dist/chunks/{chunk.JVUHDRTK.js → chunk.MYCDEEQF.js} +1 -1
  15. package/dist/chunks/{chunk.3LBUXCAP.js → chunk.N5KXL4J6.js} +1 -1
  16. package/dist/chunks/chunk.P6GWPFCT.js +32 -0
  17. package/dist/chunks/{chunk.BD4Q32VT.js → chunk.R44PTHOJ.js} +4 -4
  18. package/dist/chunks/{chunk.2Y2ST5QL.js → chunk.WELXILIP.js} +1 -1
  19. package/dist/chunks/{chunk.7BVHA4G6.js → chunk.X354U5TL.js} +3 -3
  20. package/dist/chunks/{chunk.MZOQNTPX.js → chunk.ZGC6XD6L.js} +37 -56
  21. package/dist/chunks/chunk.ZKUACKKG.js +14 -0
  22. package/dist/components/consent/consent.component.d.ts +0 -3
  23. package/dist/components/consent/consent.component.js +1 -1
  24. package/dist/components/consent/consent.js +1 -1
  25. package/dist/components/consent/consent.styles.js +1 -1
  26. package/dist/components/file-input-dropzone/file-input-dropzone.component.d.ts +0 -2
  27. package/dist/components/file-input-dropzone/file-input-dropzone.component.js +1 -1
  28. package/dist/components/file-input-dropzone/file-input-dropzone.js +1 -1
  29. package/dist/components/file-input-dropzone/file-input-dropzone.styles.js +1 -1
  30. package/dist/components/header/header.component.d.ts +31 -6
  31. package/dist/components/header/header.component.js +1 -1
  32. package/dist/components/header/header.js +1 -1
  33. package/dist/components/header/header.styles.js +1 -1
  34. package/dist/components/list-card/list-card.component.d.ts +0 -1
  35. package/dist/components/list-card/list-card.component.js +1 -1
  36. package/dist/components/list-card/list-card.js +1 -1
  37. package/dist/components/list-card/list-card.styles.js +1 -1
  38. package/dist/components/logo/logo.component.js +1 -1
  39. package/dist/components/logo/logo.js +1 -1
  40. package/dist/components/logo/logo.styles.js +1 -1
  41. package/dist/components/tool-menu/tool-menu.component.d.ts +0 -3
  42. package/dist/components/tool-menu/tool-menu.component.js +1 -1
  43. package/dist/components/tool-menu/tool-menu.js +1 -1
  44. package/dist/components/tool-menu/tool-menu.styles.js +1 -1
  45. package/dist/components/tracking/tracking.component.d.ts +0 -3
  46. package/dist/components/tracking/tracking.component.js +1 -1
  47. package/dist/components/tracking/tracking.js +1 -1
  48. package/dist/hotosm-ui.js +1 -1
  49. package/dist/style-core.css +1 -0
  50. package/dist/style.css +1 -1
  51. package/dist/themes/hot-wa.css +152 -115
  52. package/dist/themes/hot.css +1 -1
  53. package/package.json +4 -2
  54. package/dist/chunks/chunk.CQO3GWTI.js +0 -32
  55. package/dist/chunks/chunk.LHZ7Q2M3.js +0 -166
  56. package/dist/chunks/chunk.PBBH42B6.js +0 -6
  57. package/dist/chunks/chunk.PLECT4NZ.js +0 -20
  58. package/dist/chunks/chunk.QWZJAW2S.js +0 -105
  59. package/dist/chunks/chunk.U4AAC5ID.js +0 -239
package/README.md CHANGED
@@ -11,6 +11,9 @@
11
11
  <a href="https://github.com/hotosm/ui/actions/workflows/publish.yml" target="_blank">
12
12
  <img src="https://github.com/hotosm/ui/actions/workflows/publish.yml/badge.svg?event=release" alt="Publish">
13
13
  </a>
14
+ <a href="https://github.com/hotosm/ui/actions/workflows/playwright.yml" target="_blank">
15
+ <img src="https://github.com/hotosm/ui/actions/workflows/playwright.yml/badge.svg?branch=main" alt="Tests">
16
+ </a>
14
17
  <a href="https://github.com/hotosm/ui/actions/workflows/cdn_deploy.yml" target="_blank">
15
18
  <img src="https://github.com/hotosm/ui/actions/workflows/cdn_deploy.yml/badge.svg?branch=main" alt="CDN Deploy">
16
19
  </a>
@@ -44,176 +47,268 @@
44
47
  🎯 <strong>Roadmap / Tasks</strong>:
45
48
  <a href="https://github.com/orgs/hotosm/projects/37/views/3" target="_blank">https://github.com/orgs/hotosm/projects/37/views/3</a>
46
49
  </p>
47
-
48
-
49
50
  <!-- markdownlint-enable -->
50
51
 
51
52
  ---
52
53
 
53
- ## Shared UI Components with HOT Theming
54
-
55
- This repository contains advice on how to use the WebAwesome
56
- [Web Components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components)
57
- library, references to the HOTOSM style guide, and a few 'wrapper'
58
- components to assist development:
54
+ ## Overview
59
55
 
60
- 1. Reduced code duplication across HOT tools (we repeat a lot!)
61
- 2. Simplified developer experience to create a HOT app.
62
- 3. Reasonably consistent theming and style across tools.
56
+ Shared [Web Components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components)
57
+ built with [Lit](https://lit.dev) and themed for HOTOSM tools using
58
+ [WebAwesome](https://webawesome.com) primitives.
63
59
 
64
- The main goal of this project is not to re-invent the wheel, or add an extra burden
65
- of development and maintenance. It should include a minimal number of
66
- components, such as:
60
+ **Goals:**
67
61
 
68
- - Header, including auth flows (OSM, Google) and login info.
69
- - Sidebar with links to extra resources.
70
- - Footer with links.
62
+ - Reduce code duplication across HOT tools.
63
+ - Provide a consistent HOT look-and-feel out of the box.
64
+ - Keep the component set small and focused (header, sidebar, footer, etc.).
71
65
 
72
- ## Quick start
66
+ **WebAwesome version:** `3.2.1`
73
67
 
74
- Current HOTOSM UI version used: `0.3.1-b5`
75
- Current WebAwesome version used: `3.2.1`
68
+ ---
76
69
 
77
- ### Install WebAwesome
70
+ ## Installation
78
71
 
79
72
  ```bash
80
- pnpm install "@awesome.me/webawesome@3.2.1"
73
+ pnpm install @hotosm/ui @awesome.me/webawesome@3.2.1
81
74
  ```
82
75
 
83
76
  > [!NOTE]
84
- > It's important to pin the version of web awesome used to the version in this
85
- > library, to avoid any conflicts and incompatibilities between components.
77
+ > `@awesome.me/webawesome` is a **peer dependency**. Always pin it to the
78
+ > version shown above to avoid conflicts between HOT components and
79
+ > WebAwesome internals.
80
+
81
+ ---
86
82
 
87
- ### Install HOT Components
83
+ ## Loading Styles
88
84
 
89
- ```bash
90
- pnpm install "@hotosm/ui"
85
+ Two CSS files are published. Choose the one that fits your setup.
86
+
87
+ ### Option A - Bundled (recommended for simplicity)
88
+
89
+ A single self-contained file with WebAwesome base styles + HOT theme
90
+ already inlined. Nothing else to load.
91
+
92
+ ```html
93
+ <link rel="stylesheet"
94
+ href="https://cdn.jsdelivr.net/npm/@hotosm/ui@0.6.1/dist/style.css">
95
+ ```
96
+
97
+ Or from a bundler:
98
+
99
+ ```js
100
+ import '@hotosm/ui/dist/style.css';
91
101
  ```
92
102
 
93
- ### Configure The Styles
103
+ ### Option B - Split / CDN-optimised (recommended for multi-tool caching)
94
104
 
95
- In your `index.html` or equivalent frontend root, add the following classes
96
- to your `<html>` block:
105
+ If you run several HOT tools (FMTM, Tasking Manager, etc.) and want
106
+ the browser to **cache WebAwesome CSS once** across all of them, load
107
+ WebAwesome from CDN separately and use the slim HOT-only stylesheet:
97
108
 
98
- ```html
99
- <!DOCTYPE html>
100
- <html class="wa-theme-default wa-palette-hotosm wa-brand-red wa-neutral-gray
101
- wa-success-cyan wa-warning-yellow wa-danger-orange">
102
- <head>
103
- xxx
104
- </head>
105
- <body>
106
- xxx
107
- </body>
108
- </html>
109
- ```
109
+ ```html
110
+ <!-- WebAwesome CSS - shared across all HOT tools via browser cache -->
111
+ <link rel="stylesheet"
112
+ href="https://cdn.jsdelivr.net/npm/@awesome.me/webawesome@3.2.1/dist/styles/native.css">
113
+ <link rel="stylesheet"
114
+ href="https://cdn.jsdelivr.net/npm/@awesome.me/webawesome@3.2.1/dist/styles/utilities.css">
115
+ <link rel="stylesheet"
116
+ href="https://cdn.jsdelivr.net/npm/@awesome.me/webawesome@3.2.1/dist/styles/themes/default.css">
117
+
118
+ <!-- HOT theme only (fonts + design tokens + WebAwesome overrides) -->
119
+ <link rel="stylesheet"
120
+ href="https://cdn.jsdelivr.net/npm/@hotosm/ui@0.6.1/dist/style-core.css">
121
+ ```
122
+
123
+ Or from a bundler:
124
+
125
+ ```js
126
+ // WebAwesome (resolve from node_modules)
127
+ import '@awesome.me/webawesome/dist/styles/native.css';
128
+ import '@awesome.me/webawesome/dist/styles/utilities.css';
129
+ import '@awesome.me/webawesome/dist/styles/themes/default.css';
130
+
131
+ // HOT theme only
132
+ import '@hotosm/ui/dist/style-core.css';
133
+ ```
134
+
135
+ | File | Size | Contains |
136
+ | --- | --- | --- |
137
+ | `style.css` | ~105 KB | WebAwesome + fonts + HOT theme (everything) |
138
+ | `style-core.css` | ~21 KB | Fonts + HOT theme only (no WebAwesome) |
139
+
140
+ > [!TIP]
141
+ > **Which should I pick?**
142
+ >
143
+ > - Building a single standalone app? Use **Option A** - zero config.
144
+ > - Running multiple HOT tools on the same domain or expecting users to
145
+ > navigate between them? Use **Option B** - the ~84 KB of shared
146
+ > WebAwesome CSS is fetched once and cached by the browser.
147
+
148
+ ---
149
+
150
+ ## HTML Setup
151
+
152
+ Add the required WebAwesome classes to your `<html>` element:
153
+
154
+ ```html
155
+ <!DOCTYPE html>
156
+ <html class="wa-theme-default wa-palette-hotosm">
157
+ <head>...</head>
158
+ <body>...</body>
159
+ </html>
160
+ ```
110
161
 
111
162
  > [!IMPORTANT]
112
- > These classes determine the colour usage for different component variants,
113
- > and probably should not be changed!
163
+ > These classes activate the HOT colour palette and light theme for every
164
+ > WebAwesome component on the page. Do not remove them.
165
+
166
+ ---
167
+
168
+ ## Using Components
114
169
 
115
- And import the required styles in your `main.ts` or equivalent (we import from
116
- package rather than CDN, to allow the bundler to work its CSS tree-shaking
117
- magic).
170
+ ### Via Bundler (Vite, Webpack, etc.)
118
171
 
119
- ```js
120
- import '@hotosm/ui/dist/style.css';
121
- ```
172
+ Import individual components - your bundler will tree-shake the rest:
122
173
 
123
- If you wish to use any of the variables in `hot.css` manually, you can reference
124
- them as variables like so:
174
+ ```js
175
+ import '@hotosm/ui/dist/components/header/header.js';
176
+ ```
125
177
 
126
- ```css
127
- /* In your CSS file */
128
- some-param: var(--hot-some-param);
129
- /* See all variables in the `theme/hot.css` file */
130
- ```
178
+ ```html
179
+ <hot-header title="My App"></hot-header>
180
+ ```
131
181
 
132
- ### Use The Components
182
+ ### Via CDN / Plain HTML / HTMX
183
+
184
+ The recommended approach for HTMX and plain-HTML apps loads WebAwesome
185
+ styles and components from CDN so the browser can cache them once across
186
+ all HOT tools (Option B from the styles section above):
187
+
188
+ ```html
189
+ <!DOCTYPE html>
190
+ <html class="wa-theme-default wa-palette-hotosm">
191
+ <head>
192
+ <!-- WebAwesome CSS - cached once across all HOT tools at this WA version -->
193
+ <link rel="stylesheet"
194
+ href="https://cdn.jsdelivr.net/npm/@awesome.me/webawesome@3.2.1/dist/styles/native.css">
195
+ <link rel="stylesheet"
196
+ href="https://cdn.jsdelivr.net/npm/@awesome.me/webawesome@3.2.1/dist/styles/utilities.css">
197
+ <link rel="stylesheet"
198
+ href="https://cdn.jsdelivr.net/npm/@awesome.me/webawesome@3.2.1/dist/styles/themes/default.css">
199
+
200
+ <!-- HOT UI theme (fonts + HOT design tokens + WebAwesome overrides, ~21 KB) -->
201
+ <link rel="stylesheet"
202
+ href="https://cdn.jsdelivr.net/npm/@hotosm/ui@0.6.1/dist/style-core.css">
203
+
204
+ <!-- Import map: resolves WebAwesome bare-module specifiers
205
+ inside hotosm-ui.js -->
206
+ <!-- WA components load lazily from CDN only when first used;
207
+ no separate loader needed -->
208
+ <script type="importmap">
209
+ {
210
+ "imports": {
211
+ "@awesome.me/webawesome/dist/components/":
212
+ "https://cdn.jsdelivr.net/npm/@awesome.me/webawesome@3.2.1/dist-cdn/components/"
213
+ }
214
+ }
215
+ </script>
133
216
 
134
- #### Via Bundler
217
+ <!-- HOT UI web components -->
218
+ <script type="module"
219
+ src="https://cdn.jsdelivr.net/npm/@hotosm/ui@0.6.1/dist/hotosm-ui.js">
220
+ </script>
221
+ </head>
135
222
 
136
- Import each component individually into your code:
223
+ <body>
224
+ <hot-header id="hdr" title="My App" size="small" show-login></hot-header>
137
225
 
138
- ```html
139
226
  <script>
140
- import '@hotosm/ui/dist/components/header/header.js';
227
+ // Boolean props default to false.
228
+ // Include the attribute to make it true.
229
+ const hdr = document.getElementById('hdr');
230
+ hdr.drawer = true; // enable the hamburger drawer
231
+ hdr.borderBottom = true; // enable bottom border
141
232
  </script>
233
+ </body>
234
+ </html>
235
+ ```
142
236
 
143
- <hot-header
144
- param1=""
145
- param2=""
146
- >
147
- </hot-header>
148
- ```
149
-
150
- #### Via CDN
151
-
152
- If you are working directly in HTML, or other ways without a configured
153
- bundler, you can import all the components as a bundle, as use them like so:
154
-
155
- ```html
156
- <!-- The importmap is required to resolve @awesome.me/webawesome
157
- imports in hotosm/ui -->
158
- <!-- It's also vital to import from dist-cdn to avoid
159
- needing to import subdependencies for webawesome -->
160
- <head>
161
- <script type="importmap">
162
- {
163
- "imports": {
164
- "@awesome.me/webawesome/dist/components/": "https://cdn.jsdelivr.net/npm/@awesome.me/webawesome@3.0.0/dist-cdn/components/"
165
- }
166
- }
167
- </script>
168
- <script
169
- type="module"
170
- src="https://cdn.jsdelivr.net/npm/@awesome.me/webawesome@3.0.0/dist-cdn/webawesome.loader.js"
171
- ></script>
172
- <script
173
- type="module"
174
- src="https://cdn.jsdelivr.net/npm/@hotosm/ui@0.4.0/dist/hotosm-ui.js"
175
- ></script>
176
- </head>
177
-
178
- <body>
179
- <hot-header title="Test App" size="small" show-login="false"></hot-header>
180
- </body>
181
- ```
182
-
183
- #### React
184
-
185
- Import in the same way as the bundler example above, except events
186
- are bound to slightly differently:
187
-
188
- ```jsx
189
- <hot-header
190
- title="Test App"
191
- size="small"
192
- showLogin="false"
193
- onLogin={() => {
194
- console.log('handler');
195
- }}
196
- ></hot-header>
197
- ```
198
-
199
- ### Component Guides
200
-
201
- - Header: see [`/header.md`](/header.md) for detailed integration, styling, and
202
- framework examples.
203
-
204
- ### How to contribute
205
-
206
- - Clone the project `git clone git@github.com:hotosm/ui.git`
207
- - Install dependencies `pnpm install`
208
- - Run the storybook `pnpm run dev`
209
- - Write code!
210
-
211
- For **styling**, check `/theme`:
212
-
213
- - `hot.css` has a HOT theme, used across HOT components.
214
- - `hot-wa.css` has a WebAwesome theme, re-defining style variables.
215
-
216
- ### License
217
-
218
- HOT UI is free and open source software! you may use any HOT UI project under the
219
- terms of the GNU Affero General Public License (AGPL) Version 3.
237
+ > [!TIP]
238
+ > If you need only a single self-contained stylesheet instead of the three
239
+ > WA links above, you can use Option A (`style.css`) - but then the ~84 KB
240
+ > of WebAwesome CSS is bundled into the HOT package and cannot be shared
241
+ > across tools as a single cached resource.
242
+
243
+ ### React
244
+
245
+ Web Components work in React with a small caveat - use `ref` callbacks
246
+ for custom events if React's synthetic event system doesn't forward them:
247
+
248
+ ```jsx
249
+ import '@hotosm/ui/dist/components/header/header.js';
250
+
251
+ function App() {
252
+ return (
253
+ <hot-header
254
+ title="My App"
255
+ ref={(el) => {
256
+ if (el) el.addEventListener('login', () => console.log('logged in'));
257
+ }}
258
+ />
259
+ );
260
+ }
261
+ ```
262
+
263
+ ---
264
+
265
+ ## Using HOT Design Tokens
266
+
267
+ The HOT theme exposes CSS custom properties you can reference in your
268
+ own stylesheets:
269
+
270
+ ```css
271
+ .my-card {
272
+ background: var(--hot-color-primary-50);
273
+ color: var(--hot-color-neutral-900);
274
+ font-family: var(--hot-font-sans);
275
+ padding: var(--hot-spacing-medium);
276
+ border-radius: var(--hot-border-radius-large);
277
+ }
278
+ ```
279
+
280
+ See all available tokens in
281
+ [`src/themes/hot.css`](src/themes/hot.css).
282
+
283
+ ---
284
+
285
+ ## Component Guides
286
+
287
+ - **Header:** see [`header.md`](header.md) for detailed integration,
288
+ styling, and framework examples.
289
+
290
+ ---
291
+
292
+ ## Contributing
293
+
294
+ ```bash
295
+ git clone git@github.com:hotosm/ui.git
296
+ cd ui
297
+ pnpm install
298
+ pnpm run dev # starts Storybook on localhost:3001
299
+ ```
300
+
301
+ **Styling files** (under `src/themes/`):
302
+
303
+ | File | Purpose |
304
+ | --- | --- |
305
+ | `hot.css` | HOT design tokens (colours, typography, spacing) |
306
+ | `hot-wa.css` | WebAwesome variable overrides to apply the HOT palette |
307
+
308
+ ---
309
+
310
+ ## License
311
+
312
+ HOT UI is free and open source software. You may use any HOT UI project
313
+ under the terms of the
314
+ [GNU Affero General Public License (AGPL) Version 3](LICENSE.md).
@@ -45,7 +45,7 @@ import{a as o}from"./chunk.QABMDRWR.js";var e=o`
45
45
  }
46
46
 
47
47
  .dropzone:focus {
48
- outline: 2px solid var(--hot-color-red-600);
48
+ outline: 2px solid var(--wa-color-focus);
49
49
  outline-offset: 2px;
50
50
  }
51
51
 
@@ -54,13 +54,13 @@ import{a as o}from"./chunk.QABMDRWR.js";var e=o`
54
54
  }
55
55
 
56
56
  .dropzone:focus-visible {
57
- outline: 2px solid var(--hot-color-red-600);
57
+ outline: 2px solid var(--wa-color-focus);
58
58
  outline-offset: 2px;
59
59
  }
60
60
 
61
61
  .dropzone--dragging {
62
- border-color: var(--hot-color-red-500);
63
- background-color: var(--hot-color-red-100);
62
+ border-color: var(--hot-color-primary-500);
63
+ background-color: var(--hot-color-primary-100);
64
64
  border-width: 3px;
65
65
  }
66
66
 
@@ -96,11 +96,11 @@ import{a as o}from"./chunk.QABMDRWR.js";var e=o`
96
96
  }
97
97
 
98
98
  .dropzone:hover:not(.dropzone--disabled) .dropzone-icon {
99
- color: var(--hot-color-red-500);
99
+ color: var(--hot-color-primary-500);
100
100
  }
101
101
 
102
102
  .dropzone--dragging .dropzone-icon {
103
- color: var(--hot-color-red-600);
103
+ color: var(--hot-color-primary-600);
104
104
  }
105
105
 
106
106
  .dropzone-cta {
@@ -110,7 +110,7 @@ import{a as o}from"./chunk.QABMDRWR.js";var e=o`
110
110
 
111
111
  .dropzone-text {
112
112
  font-size: var(--hot-font-size-small);
113
- font-weight: 500;
113
+ font-weight: var(--hot-font-weight-semibold);
114
114
  color: var(--hot-color-neutral-700);
115
115
  text-align: center;
116
116
  }
@@ -152,15 +152,15 @@ import{a as o}from"./chunk.QABMDRWR.js";var e=o`
152
152
  gap: var(--hot-spacing-x-small);
153
153
  margin-top: var(--hot-spacing-small);
154
154
  padding: var(--hot-spacing-small);
155
- background-color: var(--hot-color-red-50);
156
- border: 1px solid var(--hot-color-red-200);
155
+ background-color: var(--hot-color-danger-50);
156
+ border: 1px solid var(--hot-color-danger-200);
157
157
  border-radius: var(--hot-border-radius-small);
158
- color: var(--hot-color-red-700);
158
+ color: var(--hot-color-danger-700);
159
159
  font-size: var(--hot-font-size-small);
160
160
  }
161
161
 
162
162
  .error-message wa-icon {
163
- color: var(--hot-color-red-600);
163
+ color: var(--hot-color-danger-600);
164
164
  font-size: var(--hot-font-size-medium);
165
165
  }
166
166
 
@@ -1 +1 @@
1
- import{b as o}from"./chunk.CQO3GWTI.js";var t=o;customElements.get("hotosm-tool-menu")||customElements.define("hotosm-tool-menu",o);export{t as a};
1
+ import{b as o}from"./chunk.P6GWPFCT.js";var t=o;customElements.get("hotosm-tool-menu")||customElements.define("hotosm-tool-menu",o);export{t as a};
@@ -0,0 +1,105 @@
1
+ import{a as f,b as m,c as z}from"./chunk.ZETV7TDG.js";import{a as M}from"./chunk.252GL6HT.js";import{d as h,e as x,f as F}from"./chunk.QABMDRWR.js";import{d as n,e as o,f as d,g as c}from"./chunk.N2W26RCT.js";var E={ATTRIBUTE:1,CHILD:2,PROPERTY:3,BOOLEAN_ATTRIBUTE:4,EVENT:5,ELEMENT:6},y=l=>(...a)=>({_$litDirective$:l,values:a}),$=class{constructor(a){}get _$AU(){return this._$AM._$AU}_$AT(a,e,i){this._$Ct=a,this._$AM=e,this._$Ci=i}_$AS(a,e){return this.update(a,e)}update(a,e){return this.render(...e)}};var _=y(class extends ${constructor(l){var a;if(super(l),l.type!==E.ATTRIBUTE||l.name!=="class"||((a=l.strings)==null?void 0:a.length)>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(l){return" "+Object.keys(l).filter(a=>l[a]).join(" ")+" "}update(l,[a]){var i,s;if(this.st===void 0){this.st=new Set,l.strings!==void 0&&(this.nt=new Set(l.strings.join(" ").split(/\s/).filter(t=>t!=="")));for(let t in a)a[t]&&!((i=this.nt)!=null&&i.has(t))&&this.st.add(t);return this.render(a)}let e=l.element.classList;for(let t of this.st)t in a||(e.remove(t),this.st.delete(t));for(let t in a){let p=!!a[t];p===this.st.has(t)||(s=this.nt)!=null&&s.has(t)||(p?(e.add(t),this.st.add(t)):(e.remove(t),this.st.delete(t)))}return x}});Promise.allSettled([import("@awesome.me/webawesome/dist/components/icon/icon.js"),import("@awesome.me/webawesome/dist/components/divider/divider.js")]);var S,T,C,D,P,A,B,L,k,W,I,O,r=class extends F{constructor(){super(...arguments);this.name="hot-file-input-dropzone";d(this,S,!1);d(this,T,"");d(this,C,0);d(this,D,!1);d(this,P,!1);d(this,A,"");d(this,B,"traditional");d(this,L,!1);d(this,k,[]);d(this,W,"");d(this,I,"");d(this,O)}get multiple(){return o(this,S)}set multiple(e){c(this,S,e)}get accept(){return o(this,T)}set accept(e){c(this,T,e)}get maxSize(){return o(this,C)}set maxSize(e){c(this,C,e)}get showPreview(){return o(this,D)}set showPreview(e){c(this,D,e)}get disabled(){return o(this,P)}set disabled(e){c(this,P,e)}get label(){return o(this,A)}set label(e){c(this,A,e)}get variant(){return o(this,B)}set variant(e){c(this,B,e)}get isDragging(){return o(this,L)}set isDragging(e){c(this,L,e)}get selectedFiles(){return o(this,k)}set selectedFiles(e){c(this,k,e)}get errorMessage(){return o(this,W)}set errorMessage(e){c(this,W,e)}get statusMessage(){return o(this,I)}set statusMessage(e){c(this,I,e)}get fileInput(){return o(this,O)}set fileInput(e){c(this,O,e)}_generateId(){return`file-${Date.now()}-${Math.random().toString(36).substring(2,11)}`}_handleDragOver(e){e.preventDefault(),e.stopPropagation(),this.disabled||(this.isDragging=!0)}_handleDragLeave(e){e.preventDefault(),e.stopPropagation(),this.isDragging=!1}_handleDrop(e){var s;if(e.preventDefault(),e.stopPropagation(),this.isDragging=!1,this.disabled)return;let i=Array.from(((s=e.dataTransfer)==null?void 0:s.files)||[]);this._processFiles(i)}_handleClick(){this.disabled||this.fileInput.click()}_handleKeyDown(e){!this.disabled&&(e.key==="Enter"||e.key===" ")&&(e.preventDefault(),this.fileInput.click())}_handleFileInputChange(e){let i=e.target,s=Array.from(i.files||[]);this._processFiles(s)}_processFiles(e){if(this.errorMessage="",!this.multiple&&e.length>1){this.errorMessage="Only one file can be selected";return}if(this.accept){let t=this.accept.split(",").map(u=>u.trim().toLowerCase()),p=e.filter(u=>{let b=u.name.toLowerCase(),g=u.type.toLowerCase();return!t.some(v=>{if(v.startsWith("."))return b.endsWith(v);if(v.endsWith("/*")){let w=v.split("/")[0];if(g)return g.startsWith(w+"/");let j=b.substring(b.lastIndexOf("."));return this._getFileIcon(j).includes(w)}return g?g===v:!1})});if(p.length>0){this.errorMessage=`Invalid file type(s): ${p.map(u=>u.name).join(", ")}`;return}}if(this.maxSize>0){let t=e.filter(p=>p.size>this.maxSize);if(t.length>0){this.errorMessage=`File(s) exceed maximum size of ${this._formatFileSize(this.maxSize)}: ${t.map(p=>p.name).join(", ")}`;return}}let i=e.map(t=>({file:t,id:this._generateId()}));this.multiple?this.selectedFiles=[...i,...this.selectedFiles]:this.selectedFiles=i,this.dispatchEvent(new CustomEvent("hot-file-change",{detail:{files:this.selectedFiles.map(t=>t.file)},bubbles:!0,composed:!0})),this.dispatchEvent(new CustomEvent("hot-files-selected",{detail:{files:i.map(t=>t.file),allFiles:this.selectedFiles.map(t=>t.file)},bubbles:!0,composed:!0}));let s=i.length;this.statusMessage=`${s} ${s===1?"file":"files"} selected: ${i.map(t=>t.file.name).join(", ")}`}_removeFile(e){let i=this.selectedFiles.find(s=>s.id===e);this.selectedFiles=this.selectedFiles.filter(s=>s.id!==e),this.fileInput&&(this.fileInput.value=""),this.dispatchEvent(new CustomEvent("hot-file-change",{detail:{files:this.selectedFiles.map(s=>s.file)},bubbles:!0,composed:!0})),i&&(this.statusMessage=`File removed: ${i.file.name}. ${this.selectedFiles.length} ${this.selectedFiles.length===1?"file":"files"} remaining.`)}_formatFileSize(e){if(e===0)return"0 Bytes";let i=1e3,s=["Bytes","KB","MB","GB"],t=Math.floor(Math.log(e)/Math.log(i)),p=e/Math.pow(i,t);return`${Math.round(p*10)/10} ${s[t]}`}_getFileIcon(e){return e.startsWith("image/")?"file-image":e.startsWith("video/")?"file-video":e.startsWith("audio/")?"file-audio":e==="application/pdf"?"file-pdf":e.includes("word")?"file-word":e.includes("sheet")||e.includes("excel")?"file-excel":e.includes("zip")||e.includes("rar")||e.includes("7z")?"file-archive":e.includes("text")?"file-text":"file"}clearFiles(){this.selectedFiles=[],this.errorMessage="",this.fileInput&&(this.fileInput.value="")}getFiles(){return this.selectedFiles.map(e=>e.file)}render(){let e={dropzone:!0,"dropzone--dragging":this.isDragging,"dropzone--disabled":this.disabled,"dropzone--has-files":this.selectedFiles.length>0,"dropzone--compact":this.variant==="compact"},i=this.variant==="compact";return h`
2
+ <div class="file-input-dropzone">
3
+ ${this.label&&this.variant==="traditional"?h`<label class="label" id="dropzone-label">${this.label}</label>`:""}
4
+ ${this.variant==="traditional"?h`<wa-divider></wa-divider>`:""}
5
+
6
+ <div
7
+ class=${_(e)}
8
+ role="button"
9
+ tabindex=${this.disabled?"-1":"0"}
10
+ aria-disabled=${this.disabled}
11
+ aria-label=${this.label||"File upload"}
12
+ aria-describedby="dropzone-description${this.accept||this.maxSize?" dropzone-hints":""}"
13
+ @dragover=${this._handleDragOver}
14
+ @dragleave=${this._handleDragLeave}
15
+ @drop=${this._handleDrop}
16
+ @click=${this._handleClick}
17
+ @keydown=${this._handleKeyDown}
18
+ >
19
+ <input
20
+ type="file"
21
+ class="file-uploader"
22
+ ?multiple=${this.multiple}
23
+ accept=${this.accept}
24
+ ?disabled=${this.disabled}
25
+ @change=${this._handleFileInputChange}
26
+ aria-hidden="true"
27
+ tabindex="-1"
28
+ />
29
+
30
+ <div class="dropzone-content">
31
+ <wa-icon
32
+ name="${i?"arrow-up-from-bracket":"cloud-arrow-up"}"
33
+ class="dropzone-icon"
34
+ aria-hidden="true"
35
+ ></wa-icon>
36
+ <div class="dropzone-text">
37
+ ${i?h`<div class="compact-text" id="dropzone-description">
38
+ ${this.label}
39
+ </div>`:this.isDragging?h`<div id="dropzone-description">
40
+ Drop ${this.multiple?"files":"file"} here
41
+ </div>`:h`
42
+ <div class="dropzone-cta" id="dropzone-description">
43
+ <div>
44
+ Drop ${this.multiple?"files":"file"} here or&nbsp;
45
+ </div>
46
+ <div class="browse">browse</div>
47
+ </div>
48
+ ${this.accept||this.maxSize?h`<div id="dropzone-hints">
49
+ ${this.accept?h`<div class="dropzone-accept">
50
+ ${this.accept.split(",").join(", ")} only
51
+ </div>`:""}
52
+ ${this.maxSize?h`<div class="dropzone-maxsize">
53
+ Max size: ${this._formatFileSize(this.maxSize)}
54
+ </div>`:""}
55
+ </div>`:""}
56
+ `}
57
+ </div>
58
+ </div>
59
+ </div>
60
+ ${this.errorMessage?h`
61
+ <div class="error-message" role="alert">
62
+ <wa-icon name="exclamation-circle"></wa-icon>
63
+ ${this.errorMessage}
64
+ </div>
65
+ `:""}
66
+ ${this.showPreview&&this.selectedFiles.length>0?h`
67
+ <div class="file-preview-list">
68
+ ${this.selectedFiles.map(({file:s,id:t})=>h`
69
+ <hot-list-card
70
+ title=${s.name}
71
+ subtitle=${this._formatFileSize(s.size)}
72
+ icon=${this._getFileIcon(s.type)}
73
+ itemId=${t}
74
+ @hot-remove=${()=>this._removeFile(t)}
75
+ ></hot-list-card>
76
+ `)}
77
+ </div>
78
+ `:""}
79
+
80
+ <div
81
+ class="sr-only"
82
+ role="status"
83
+ aria-live="polite"
84
+ aria-atomic="true"
85
+ >
86
+ ${this.statusMessage}
87
+ </div>
88
+ </div>
89
+ `}};S=new WeakMap,T=new WeakMap,C=new WeakMap,D=new WeakMap,P=new WeakMap,A=new WeakMap,B=new WeakMap,L=new WeakMap,k=new WeakMap,W=new WeakMap,I=new WeakMap,O=new WeakMap,r.styles=[M],n([f({type:Boolean})],r.prototype,"multiple",1),n([f({type:String})],r.prototype,"accept",1),n([f({type:Number})],r.prototype,"maxSize",1),n([f({type:Boolean,attribute:"show-preview"})],r.prototype,"showPreview",1),n([f({type:Boolean})],r.prototype,"disabled",1),n([f({type:String})],r.prototype,"label",1),n([f({type:String})],r.prototype,"variant",1),n([m()],r.prototype,"isDragging",1),n([m()],r.prototype,"selectedFiles",1),n([m()],r.prototype,"errorMessage",1),n([m()],r.prototype,"statusMessage",1),n([z('input[type="file"]')],r.prototype,"fileInput",1);var Z=r;export{r as a,Z as b};
90
+ /*! Bundled license information:
91
+
92
+ lit-html/directive.js:
93
+ (**
94
+ * @license
95
+ * Copyright 2017 Google LLC
96
+ * SPDX-License-Identifier: BSD-3-Clause
97
+ *)
98
+
99
+ lit-html/directives/class-map.js:
100
+ (**
101
+ * @license
102
+ * Copyright 2018 Google LLC
103
+ * SPDX-License-Identifier: BSD-3-Clause
104
+ *)
105
+ */