@woylie/doggo 0.16.1 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -1
- package/assets/js/hooks/dialog.js +3 -3
- package/assets/js/hooks/toggletip.js +82 -0
- package/assets/js/index.js +1 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -21,11 +21,21 @@ The package can be installed by adding `doggo` to your list of dependencies in
|
|
|
21
21
|
```elixir
|
|
22
22
|
def deps do
|
|
23
23
|
[
|
|
24
|
-
{:doggo, "~> 0.
|
|
24
|
+
{:doggo, "~> 0.17.0"}
|
|
25
25
|
]
|
|
26
26
|
end
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
Enable diagnostic checks at render time in `config/dev.exs` and
|
|
30
|
+
`config/test.exs` :
|
|
31
|
+
|
|
32
|
+
```elixir
|
|
33
|
+
config :doggo, :diagnostics, true
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
These checks raise errors if components are not wired up correctly, for example
|
|
37
|
+
if an accessible name is not set. If disabled, the checks are not compiled.
|
|
38
|
+
|
|
29
39
|
### Compatibility
|
|
30
40
|
|
|
31
41
|
This package is tested against the Elixir and OTP versions that are still
|
|
@@ -172,6 +182,7 @@ import {
|
|
|
172
182
|
MenuButton,
|
|
173
183
|
SplitPane,
|
|
174
184
|
Tabs,
|
|
185
|
+
Toggletip,
|
|
175
186
|
Toolbar,
|
|
176
187
|
Tooltip,
|
|
177
188
|
Tree,
|
|
@@ -186,6 +197,7 @@ const hooks = {
|
|
|
186
197
|
"Doggo.MenuButton": MenuButton,
|
|
187
198
|
"Doggo.SplitPane": SplitPane,
|
|
188
199
|
"Doggo.Tabs": Tabs,
|
|
200
|
+
"Doggo.Toggletip": Toggletip,
|
|
189
201
|
"Doggo.Toolbar": Toolbar,
|
|
190
202
|
"Doggo.Tooltip": Tooltip,
|
|
191
203
|
"Doggo.Tree": Tree,
|
|
@@ -215,6 +227,7 @@ import {
|
|
|
215
227
|
MenuButton,
|
|
216
228
|
SplitPane,
|
|
217
229
|
Tabs,
|
|
230
|
+
Toggletip,
|
|
218
231
|
Toolbar,
|
|
219
232
|
Tooltip,
|
|
220
233
|
Tree,
|
|
@@ -231,6 +244,7 @@ import {
|
|
|
231
244
|
"Doggo.MenuButton": MenuButton,
|
|
232
245
|
"Doggo.SplitPane": SplitPane,
|
|
233
246
|
"Doggo.Tabs": Tabs,
|
|
247
|
+
"Doggo.Toggletip": Toggletip,
|
|
234
248
|
"Doggo.Toolbar": Toolbar,
|
|
235
249
|
"Doggo.Tooltip": Tooltip,
|
|
236
250
|
"Doggo.Tree": Tree,
|
|
@@ -328,6 +342,35 @@ The `data-invalid` attribute is set on the field wrapper whenever the field has
|
|
|
328
342
|
errors. The `:empty` selector does not work here, because the rendered list
|
|
329
343
|
contains whitespace.
|
|
330
344
|
|
|
345
|
+
### Text direction
|
|
346
|
+
|
|
347
|
+
Some components render text you pass in an attribute next to text of their
|
|
348
|
+
own, such as a switch label beside its state text. These elements have
|
|
349
|
+
`dir="auto"`. The browser takes their direction from the text itself, and
|
|
350
|
+
right-to-left text in a left-to-right page does not reorder the text around
|
|
351
|
+
it.
|
|
352
|
+
|
|
353
|
+
Text you pass in a slot is not automatically isolated. If it can hold text in
|
|
354
|
+
another direction, wrap it in `<bdi>`.
|
|
355
|
+
|
|
356
|
+
```heex
|
|
357
|
+
<.steps current_step={1}>
|
|
358
|
+
<:step><bdi>{@first_step_name}</bdi></:step>
|
|
359
|
+
<:step><bdi>{@second_step_name}</bdi></:step>
|
|
360
|
+
</.steps>
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
The `field` label holds your label text next to the required or optional
|
|
364
|
+
mark, without an element of its own. If your labels can be in a different
|
|
365
|
+
direction than the page, set `unicode-bidi: plaintext` on the label. The
|
|
366
|
+
browser then takes the label's direction from its content.
|
|
367
|
+
|
|
368
|
+
```css
|
|
369
|
+
.field label {
|
|
370
|
+
unicode-bidi: plaintext;
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
331
374
|
## Design decisions
|
|
332
375
|
|
|
333
376
|
- Favor semantic HTML elements over CSS classes for structure and clarity.
|
|
@@ -338,6 +381,8 @@ contains whitespace.
|
|
|
338
381
|
attributes.
|
|
339
382
|
- The library is designed without default styles and does not prefer any
|
|
340
383
|
particular CSS framework.
|
|
384
|
+
- Don't render a container if it doesn't have any children, for example a
|
|
385
|
+
navigation without links.
|
|
341
386
|
|
|
342
387
|
## Demo app
|
|
343
388
|
|
|
@@ -400,6 +445,7 @@ component at any level:
|
|
|
400
445
|
|
|
401
446
|
- Adding an attribute.
|
|
402
447
|
- Adding a class.
|
|
448
|
+
- Changing the whitespace between elements.
|
|
403
449
|
- Removing an attribute that normally no CSS styles are attached to.
|
|
404
450
|
|
|
405
451
|
These changes **are** breaking:
|
|
@@ -413,6 +459,8 @@ These changes **are** breaking:
|
|
|
413
459
|
- Reordering elements.
|
|
414
460
|
- Renaming a JavaScript hook.
|
|
415
461
|
|
|
462
|
+
The output format of `mix dog.safelist` only changes in a major release.
|
|
463
|
+
|
|
416
464
|
## Feedback
|
|
417
465
|
|
|
418
466
|
If you encounter any issues with a component, have suggestions for improvements,
|
|
@@ -9,7 +9,7 @@ const hasClosedBy = () => "closedBy" in HTMLDialogElement.prototype;
|
|
|
9
9
|
export function initDialog(dialog, { execJS = () => {} } = {}) {
|
|
10
10
|
let invoke;
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const closedBy = () => dialog.getAttribute("closedby");
|
|
13
13
|
|
|
14
14
|
// Dispatched by `Doggo.show_modal/2`.
|
|
15
15
|
dialog.addEventListener("doggo:open", () => {
|
|
@@ -47,13 +47,13 @@ export function initDialog(dialog, { execJS = () => {} } = {}) {
|
|
|
47
47
|
// `closedby="none"` has to hold Escape as well, which is the one part of
|
|
48
48
|
// the attribute that native `<dialog>` does not give us anyway.
|
|
49
49
|
dialog.addEventListener("cancel", (e) => {
|
|
50
|
-
if (
|
|
50
|
+
if (closedBy() === "none") e.preventDefault();
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
// A click on the backdrop reaches the dialog itself, never a child, so
|
|
54
54
|
// the target is the test for being outside.
|
|
55
55
|
dialog.addEventListener("click", (e) => {
|
|
56
|
-
if (
|
|
56
|
+
if (closedBy() === "any" && e.target === dialog) dialog.close();
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// A long announcement cannot be interrupted, so the text is cut. The region is
|
|
2
|
+
// emptied afterwards, since the open panel holds the same text.
|
|
3
|
+
const maxLength = 250;
|
|
4
|
+
const clearAfter = 1000;
|
|
5
|
+
|
|
6
|
+
const announcement = (panel) => {
|
|
7
|
+
const text = panel.textContent.replace(/\s+/g, " ").trim();
|
|
8
|
+
return text.length > maxLength ? `${text.slice(0, maxLength)}…` : text;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// Remove the positioning once CSS anchor positioning is widely baseline
|
|
12
|
+
// available; the example CSS places the panel where it is supported.
|
|
13
|
+
const anchored = () => window.CSS?.supports?.("position-area: block-end");
|
|
14
|
+
|
|
15
|
+
export function initToggletip(toggletip) {
|
|
16
|
+
const button = toggletip.querySelector(":scope > button");
|
|
17
|
+
const panel = toggletip.querySelector(":scope > [popover]");
|
|
18
|
+
const status = toggletip.querySelector(':scope > [role="status"]');
|
|
19
|
+
let timer;
|
|
20
|
+
|
|
21
|
+
const place = () => {
|
|
22
|
+
const rect = button.getBoundingClientRect();
|
|
23
|
+
const rtl = getComputedStyle(toggletip).direction === "rtl";
|
|
24
|
+
const start = rtl ? window.innerWidth - rect.right : rect.left;
|
|
25
|
+
|
|
26
|
+
const style = {
|
|
27
|
+
inset: "auto",
|
|
28
|
+
margin: "0",
|
|
29
|
+
"margin-block-start": "var(--toggletip-offset)",
|
|
30
|
+
"inset-block-start": `${rect.bottom}px`,
|
|
31
|
+
"inset-inline-start": `${start}px`,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
for (const [name, value] of Object.entries(style)) {
|
|
35
|
+
panel.style.setProperty(name, value);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const position = () => {
|
|
40
|
+
if (!anchored() && panel.matches(":popover-open")) place();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const beforeToggle = (e) => {
|
|
44
|
+
if (!anchored() && e.newState === "open") place();
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const announce = (e) => {
|
|
48
|
+
clearTimeout(timer);
|
|
49
|
+
status.textContent = e.newState === "open" ? announcement(panel) : "";
|
|
50
|
+
timer = setTimeout(() => (status.textContent = ""), clearAfter);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
panel.addEventListener("beforetoggle", beforeToggle);
|
|
54
|
+
panel.addEventListener("toggle", announce);
|
|
55
|
+
window.addEventListener("scroll", position, true);
|
|
56
|
+
window.addEventListener("resize", position);
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
update: position,
|
|
60
|
+
destroy() {
|
|
61
|
+
clearTimeout(timer);
|
|
62
|
+
panel.removeEventListener("beforetoggle", beforeToggle);
|
|
63
|
+
panel.removeEventListener("toggle", announce);
|
|
64
|
+
window.removeEventListener("scroll", position, true);
|
|
65
|
+
window.removeEventListener("resize", position);
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default {
|
|
71
|
+
mounted() {
|
|
72
|
+
this.instance = initToggletip(this.el);
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
updated() {
|
|
76
|
+
this.instance.update();
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
destroyed() {
|
|
80
|
+
this.instance.destroy();
|
|
81
|
+
},
|
|
82
|
+
};
|
package/assets/js/index.js
CHANGED
|
@@ -8,4 +8,5 @@ export { default as SplitPane, initSplitPane } from "./hooks/split_pane.js";
|
|
|
8
8
|
export { default as Tabs, initTabs } from "./hooks/tabs.js";
|
|
9
9
|
export { default as Tree, initTree } from "./hooks/tree.js";
|
|
10
10
|
export { default as Toolbar, initToolbar } from "./hooks/toolbar.js";
|
|
11
|
+
export { default as Toggletip, initToggletip } from "./hooks/toggletip.js";
|
|
11
12
|
export { default as Tooltip, initTooltip } from "./hooks/tooltip.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@woylie/doggo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "JavaScript hooks for the Doggo component library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"phoenix",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"./combobox": "./assets/js/hooks/combobox.js",
|
|
30
30
|
"./tabs": "./assets/js/hooks/tabs.js",
|
|
31
31
|
"./toolbar": "./assets/js/hooks/toolbar.js",
|
|
32
|
+
"./toggletip": "./assets/js/hooks/toggletip.js",
|
|
32
33
|
"./tooltip": "./assets/js/hooks/tooltip.js",
|
|
33
34
|
"./menu": "./assets/js/hooks/menu.js",
|
|
34
35
|
"./menu_button": "./assets/js/hooks/menu_button.js",
|