proscenium 0.19.0.beta16-arm64-darwin → 0.19.0.test1-arm64-darwin
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.
- checksums.yaml +4 -4
- data/README.md +44 -0
- data/lib/proscenium/builder.rb +1 -1
- data/lib/proscenium/bundled_gems.rb +1 -1
- data/lib/proscenium/css_module.rb +5 -0
- data/lib/proscenium/middleware.rb +6 -0
- data/lib/proscenium/monkey.rb +9 -2
- data/lib/proscenium/phlex.rb +6 -0
- data/lib/proscenium/railtie.rb +22 -2
- data/lib/proscenium/react_componentable.rb +1 -1
- data/lib/proscenium/registry/bundled_package.rb +29 -0
- data/lib/proscenium/registry/package.rb +95 -0
- data/lib/proscenium/registry/ruby_gem_package.rb +30 -0
- data/lib/proscenium/registry.rb +29 -0
- data/lib/proscenium/resolver.rb +9 -0
- data/lib/proscenium/ruby_gems.rb +67 -0
- data/lib/proscenium/ui/breadcrumbs/component.module.css +14 -0
- data/lib/proscenium/ui/breadcrumbs/component.rb +71 -0
- data/lib/proscenium/ui/breadcrumbs/computed_element.rb +69 -0
- data/lib/proscenium/ui/breadcrumbs/control.rb +95 -0
- data/lib/proscenium/ui/breadcrumbs/mixins.css +83 -0
- data/lib/proscenium/ui/breadcrumbs.rb +72 -0
- data/lib/proscenium/ui/component.rb +7 -0
- data/lib/proscenium/ui/custom_element.js +54 -0
- data/lib/proscenium/ui/flash/bun.lock +19 -0
- data/lib/proscenium/ui/flash/index.css +1 -0
- data/lib/proscenium/ui/flash/index.js +77 -0
- data/lib/proscenium/ui/flash/node_modules/dom-mutations/index.d.ts +33 -0
- data/lib/proscenium/ui/flash/node_modules/dom-mutations/index.js +44 -0
- data/lib/proscenium/ui/flash/node_modules/dom-mutations/license +9 -0
- data/lib/proscenium/ui/flash/node_modules/dom-mutations/package.json +59 -0
- data/lib/proscenium/ui/flash/node_modules/dom-mutations/readme.md +125 -0
- data/lib/proscenium/ui/flash/node_modules/sourdough-toast/LICENSE +20 -0
- data/lib/proscenium/ui/flash/node_modules/sourdough-toast/README.md +11 -0
- data/lib/proscenium/ui/flash/node_modules/sourdough-toast/package.json +44 -0
- data/lib/proscenium/ui/flash/node_modules/sourdough-toast/src/sourdough-toast.css +697 -0
- data/lib/proscenium/ui/flash/node_modules/sourdough-toast/src/sourdough-toast.js +537 -0
- data/lib/proscenium/ui/flash/package.json +11 -0
- data/lib/proscenium/ui/flash.rb +15 -0
- data/lib/proscenium/ui/form/field_methods.rb +88 -0
- data/lib/proscenium/ui/form/fields/base.rb +188 -0
- data/lib/proscenium/ui/form/fields/checkbox/index.jsx +48 -0
- data/lib/proscenium/ui/form/fields/checkbox/index.module.css +9 -0
- data/lib/proscenium/ui/form/fields/checkbox/previews/basic.jsx +8 -0
- data/lib/proscenium/ui/form/fields/checkbox.rb +32 -0
- data/lib/proscenium/ui/form/fields/date.module.css +27 -0
- data/lib/proscenium/ui/form/fields/datetime.rb +15 -0
- data/lib/proscenium/ui/form/fields/hidden.rb +9 -0
- data/lib/proscenium/ui/form/fields/input/index.jsx +71 -0
- data/lib/proscenium/ui/form/fields/input/index.module.css +13 -0
- data/lib/proscenium/ui/form/fields/input/previews/basic.jsx +8 -0
- data/lib/proscenium/ui/form/fields/input.rb +14 -0
- data/lib/proscenium/ui/form/fields/radio_group.rb +173 -0
- data/lib/proscenium/ui/form/fields/radio_input/index.jsx +44 -0
- data/lib/proscenium/ui/form/fields/radio_input/index.module.css +13 -0
- data/lib/proscenium/ui/form/fields/radio_input/previews/basic.jsx +8 -0
- data/lib/proscenium/ui/form/fields/radio_input.rb +17 -0
- data/lib/proscenium/ui/form/fields/rich_textarea.css +23 -0
- data/lib/proscenium/ui/form/fields/rich_textarea.js +6 -0
- data/lib/proscenium/ui/form/fields/rich_textarea.rb +18 -0
- data/lib/proscenium/ui/form/fields/select.jsx +47 -0
- data/lib/proscenium/ui/form/fields/select.module.css +46 -0
- data/lib/proscenium/ui/form/fields/select.rb +300 -0
- data/lib/proscenium/ui/form/fields/tel.css +297 -0
- data/lib/proscenium/ui/form/fields/tel.js +83 -0
- data/lib/proscenium/ui/form/fields/tel.rb +54 -0
- data/lib/proscenium/ui/form/fields/textarea/index.jsx +50 -0
- data/lib/proscenium/ui/form/fields/textarea/index.module.css +13 -0
- data/lib/proscenium/ui/form/fields/textarea/previews/basic.jsx +8 -0
- data/lib/proscenium/ui/form/fields/textarea.rb +18 -0
- data/lib/proscenium/ui/form/translation.rb +71 -0
- data/lib/proscenium/ui/form.css +52 -0
- data/lib/proscenium/ui/form.rb +211 -0
- data/lib/proscenium/ui/props.css +7 -0
- data/lib/proscenium/ui/stimulus-loading.js +65 -0
- data/lib/proscenium/ui/test.js +1 -0
- data/lib/proscenium/ui/ujs/class.js +15 -0
- data/lib/proscenium/ui/ujs/data_confirm.js +23 -0
- data/lib/proscenium/ui/ujs/data_disable_with.js +68 -0
- data/lib/proscenium/ui/ujs/index.js +9 -0
- data/lib/proscenium/ui.rb +11 -0
- data/lib/proscenium/version.rb +1 -1
- data/lib/proscenium/view_component/css_modules.rb +11 -0
- data/lib/proscenium/view_component/react_component.rb +22 -0
- data/lib/proscenium/view_component/sideload.rb +4 -0
- data/lib/proscenium/view_component.rb +36 -0
- data/lib/proscenium.rb +34 -6
- metadata +136 -24
- /data/lib/proscenium/{react-manager → ui/react-manager}/index.jsx +0 -0
- /data/lib/proscenium/{react-manager → ui/react-manager}/react.js +0 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Proscenium::UI::Breadcrumbs
|
4
|
+
# Include this module in your controller to add support for adding breadcrumb elements. You can
|
5
|
+
# then use the `add_breadcrumb` and `prepend_breadcrumb` class methods to append and/or prepend
|
6
|
+
# breadcrumb elements.
|
7
|
+
module Control
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
include ActionView::Helpers::SanitizeHelper
|
10
|
+
|
11
|
+
included do
|
12
|
+
helper_method :breadcrumbs_as_json, :breadcrumbs_for_title if respond_to?(:helper_method)
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
# Appends a new breadcrumb element into the collection.
|
17
|
+
#
|
18
|
+
# @param name [String, Symbol, Proc, #for_breadcrumb] The name or content of the breadcrumb.
|
19
|
+
# @param path [String, Symbol, Array, Proc, nil] The path (route) to use as the HREF for the
|
20
|
+
# breadcrumb.
|
21
|
+
# @param filter_options [Hash] Options to pass through to the before_action filter.
|
22
|
+
def add_breadcrumb(name, path = nil, **filter_options)
|
23
|
+
element_options = filter_options.delete(:options) || {}
|
24
|
+
|
25
|
+
before_action(filter_options) do |controller|
|
26
|
+
controller.send :add_breadcrumb, name, path, element_options
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Prepend a new breadcrumb element into the collection.
|
31
|
+
#
|
32
|
+
# @param name [String, Symbol, Proc, #for_breadcrumb] The name or content of the breadcrumb.
|
33
|
+
# @param path [String, Symbol, Array, Proc, nil] The path (route) to use as the HREF for the
|
34
|
+
# breadcrumb.
|
35
|
+
# @param filter_options [Hash] Options to pass through to the before_action filter.
|
36
|
+
def prepend_breadcrumb(name, path = nil, **filter_options)
|
37
|
+
element_options = filter_options.delete(:options) || {}
|
38
|
+
|
39
|
+
before_action(filter_options) do |controller|
|
40
|
+
controller.send :prepend_breadcrumb, name, path, element_options
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Pushes a new breadcrumb element into the collection.
|
46
|
+
#
|
47
|
+
# @param name [String, Symbol, Proc, #for_breadcrumb] The name or content of the breadcrumb.
|
48
|
+
# @param path [String, Symbol, Array, Proc, nil] The path (route) to use as the HREF for the
|
49
|
+
# breadcrumb.
|
50
|
+
# @param options [Hash]
|
51
|
+
def add_breadcrumb(name, path = nil, options = {})
|
52
|
+
breadcrumbs << Element.new(name, path, options)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Prepend a new breadcrumb element into the collection.
|
56
|
+
#
|
57
|
+
# @param name [String, Symbol, Proc, #for_breadcrumb] The name or content of the breadcrumb.
|
58
|
+
# @param path [String, Symbol, Array, Proc, nil] The path (route) to use as the HREF for the
|
59
|
+
# breadcrumb.
|
60
|
+
# @param options [Hash]
|
61
|
+
def prepend_breadcrumb(name, path = nil, options = {})
|
62
|
+
breadcrumbs.prepend Element.new(name, path, options)
|
63
|
+
end
|
64
|
+
|
65
|
+
def breadcrumbs
|
66
|
+
@breadcrumbs ||= []
|
67
|
+
end
|
68
|
+
|
69
|
+
def breadcrumbs_as_json
|
70
|
+
computed_breadcrumbs.map do |ele|
|
71
|
+
path = ele.path
|
72
|
+
|
73
|
+
{ name: ele.name, path: ele.path.nil? || helpers.current_page?(path) ? nil : path }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# @param primary [Boolean] whether to return only the primary breadcrumb.
|
78
|
+
def breadcrumbs_for_title(primary: false)
|
79
|
+
names = computed_breadcrumbs.map(&:name)
|
80
|
+
return names.pop if primary
|
81
|
+
|
82
|
+
out = [names.pop]
|
83
|
+
out << names.join(': ') unless names.empty?
|
84
|
+
strip_tags out.join(' - ')
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def computed_breadcrumbs
|
90
|
+
@computed_breadcrumbs ||= breadcrumbs.map do |ele|
|
91
|
+
ComputedElement.new ele, helpers
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
@define-mixin breadcrumbs {
|
2
|
+
/* Default properties */
|
3
|
+
--_puiBreadcrumbs--separator-color: GrayText;
|
4
|
+
--_puiBreadcrumbs--separator: url("/proscenium/icons/angle-right-regular.svg");
|
5
|
+
|
6
|
+
margin: 10px;
|
7
|
+
|
8
|
+
ol {
|
9
|
+
list-style: none;
|
10
|
+
padding: 0;
|
11
|
+
margin: 0;
|
12
|
+
display: flex;
|
13
|
+
align-items: baseline;
|
14
|
+
|
15
|
+
li {
|
16
|
+
text-transform: uppercase;
|
17
|
+
display: flex;
|
18
|
+
align-items: center;
|
19
|
+
|
20
|
+
@media (max-width: 426px) {
|
21
|
+
&:not(:nth-last-child(2)) {
|
22
|
+
display: none;
|
23
|
+
}
|
24
|
+
|
25
|
+
&:nth-last-child(2)::before {
|
26
|
+
@mixin _separator;
|
27
|
+
margin: 0 0.5rem 0 0;
|
28
|
+
transform: rotate(180deg);
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
@media (min-width: 427px) {
|
33
|
+
&:not(:last-child)::after {
|
34
|
+
@mixin _separator;
|
35
|
+
margin: 0 0.5rem;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
&:last-child {
|
40
|
+
font-weight: 500;
|
41
|
+
text-transform: none;
|
42
|
+
}
|
43
|
+
|
44
|
+
&:last-child > a {
|
45
|
+
font-weight: 500;
|
46
|
+
text-transform: none;
|
47
|
+
}
|
48
|
+
|
49
|
+
a {
|
50
|
+
color: var(--puiBreadcrumbs--link-color, revert);
|
51
|
+
display: flex;
|
52
|
+
|
53
|
+
&:hover {
|
54
|
+
color: var(--puiBreadcrumbs--link-hover-color, revert);
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
svg {
|
59
|
+
height: 1em;
|
60
|
+
width: 1em;
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
@define-mixin _separator {
|
67
|
+
display: inline-block;
|
68
|
+
content: "";
|
69
|
+
height: 1rem;
|
70
|
+
width: 1rem;
|
71
|
+
-webkit-mask: var(
|
72
|
+
--puiBreadcrumbs--separator,
|
73
|
+
var(--_puiBreadcrumbs--separator)
|
74
|
+
)
|
75
|
+
no-repeat center center;
|
76
|
+
mask: var(--puiBreadcrumbs--separator, var(--_puiBreadcrumbs--separator))
|
77
|
+
no-repeat center center;
|
78
|
+
vertical-align: sub;
|
79
|
+
background-color: var(
|
80
|
+
--puiBreadcrumbs--separator-color,
|
81
|
+
var(--_puiBreadcrumbs--separator-color)
|
82
|
+
);
|
83
|
+
}
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Proscenium::UI
|
4
|
+
# Provides breadcrumb functionality for controllers and views. Breadcrumbs are a type of
|
5
|
+
# navigation that show the user where they are in the application's hierarchy.
|
6
|
+
# The `Proscenium::UI::Breadcrumbs::Control` module provides the `add_breadcrumb` and
|
7
|
+
# `prepend_breadcrumb` class methods for adding breadcrumb elements, and is intended to be
|
8
|
+
# included in your controllers.
|
9
|
+
#
|
10
|
+
# The `add_breadcrumb` method adds a new breadcrumb element to the end of the collection, while
|
11
|
+
# the `prepend_breadcrumb` method adds a new breadcrumb element to the beginning of the
|
12
|
+
# collection. Both methods take a name, and path as arguments. The name argument is the name or
|
13
|
+
# content of the breadcrumb, while the path argument is the path (route) to use as the HREF for
|
14
|
+
# the breadcrumb.
|
15
|
+
#
|
16
|
+
# class UsersController < ApplicationController
|
17
|
+
# include Proscenium::UI::Breadcrumbs::Control
|
18
|
+
# add_breadcrumb 'Users', :users_path
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Display the breadcrumbs in your views with the breadcrumbs component.
|
22
|
+
# @see `Proscenium::UI::Breadcrumbs::Component`.
|
23
|
+
#
|
24
|
+
# At it's simplest, you can add a breadcrumb with a name of "User", and a path of "/users" like
|
25
|
+
# this:
|
26
|
+
#
|
27
|
+
# add_breadcrumb 'Foo', '/foo'
|
28
|
+
#
|
29
|
+
# The value of the path is always passed to `url_for` before being rendered. It is also optional,
|
30
|
+
# and if omitted, the breadcrumb will be rendered as plain text.
|
31
|
+
#
|
32
|
+
# Both name and path can be given a Symbol, which can be used to call a method of the same name on
|
33
|
+
# the controller. If a Symbol is given as the path, and no method of the same name exists, then
|
34
|
+
# `url_for` will be called with the Symbol as the argument. Likewise, if an Array is given as the
|
35
|
+
# path, then `url_for` will be called with the Array as the argument.
|
36
|
+
#
|
37
|
+
# If a Symbol is given as the path or name, and it begins with `@` (eg. `:@foo`), then the
|
38
|
+
# instance variable of the same name will be called.
|
39
|
+
#
|
40
|
+
# add_breadcrumb :@foo, :@bar
|
41
|
+
#
|
42
|
+
# A Proc can also be given as the name and/or path. The Proc will be called within the context of
|
43
|
+
# the controller.
|
44
|
+
#
|
45
|
+
# add_breadcrumb -> { @foo }, -> { @bar }
|
46
|
+
#
|
47
|
+
# Passing an object that responds to `#for_breadcrumb` as the name will call that method on the
|
48
|
+
# object to get the breadcrumb name.
|
49
|
+
#
|
50
|
+
module Breadcrumbs
|
51
|
+
extend ActiveSupport::Autoload
|
52
|
+
|
53
|
+
autoload :Control
|
54
|
+
autoload :ComputedElement
|
55
|
+
autoload :Component
|
56
|
+
|
57
|
+
# Represents a navigation element in the breadcrumb collection.
|
58
|
+
class Element
|
59
|
+
attr_accessor :name, :path, :options
|
60
|
+
|
61
|
+
# @param name [String] the element/link name
|
62
|
+
# @param path [String] the element/link URL
|
63
|
+
# @param options [Hash] the element/link options
|
64
|
+
# @return [Element]
|
65
|
+
def initialize(name, path = nil, options = {})
|
66
|
+
self.name = name
|
67
|
+
self.path = path
|
68
|
+
self.options = options
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
/**
|
2
|
+
* Base class for custom elements, providing support for event delegation, and idempotent
|
3
|
+
* customElement registration.
|
4
|
+
*
|
5
|
+
* The `handleEvent` method is called any time an event defined in `delegatedEvents` is triggered.
|
6
|
+
* It's a central handler to handle events for this custom element.
|
7
|
+
*
|
8
|
+
* @example
|
9
|
+
* class MyComponent extends CustomElement {
|
10
|
+
* static componentName = 'my-component'
|
11
|
+
* static delegatedEvents = ['click']
|
12
|
+
*
|
13
|
+
* handleEvent(event) {
|
14
|
+
* console.log('Hello, world!')
|
15
|
+
* }
|
16
|
+
* }
|
17
|
+
* MyComponent.register()
|
18
|
+
*/
|
19
|
+
export default class CustomElement extends HTMLElement {
|
20
|
+
/**
|
21
|
+
* Register the component as a custom element, inferring the component name from the kebab-cased
|
22
|
+
* class name. You can override the component name by setting a static `componentName` property.
|
23
|
+
*
|
24
|
+
* This method is idempotent.
|
25
|
+
*/
|
26
|
+
static register() {
|
27
|
+
if (this.componentName === undefined) {
|
28
|
+
this.componentName = this.name
|
29
|
+
.replaceAll(/(.)([A-Z])/g, "$1-$2")
|
30
|
+
.toLowerCase();
|
31
|
+
}
|
32
|
+
|
33
|
+
if (!customElements.get(this.componentName)) {
|
34
|
+
customElements.define(this.componentName, this);
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
/**
|
39
|
+
* A list of event types to be delegated for the lifetime of the custom element.
|
40
|
+
*
|
41
|
+
* @type {Array}
|
42
|
+
*/
|
43
|
+
static delegatedEvents = [];
|
44
|
+
|
45
|
+
constructor() {
|
46
|
+
super();
|
47
|
+
|
48
|
+
if (typeof this.handleEvent !== "undefined") {
|
49
|
+
this.constructor.delegatedEvents?.forEach((event) => {
|
50
|
+
this.addEventListener(event, this);
|
51
|
+
});
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
{
|
2
|
+
"lockfileVersion": 1,
|
3
|
+
"workspaces": {
|
4
|
+
"": {
|
5
|
+
"name": "@proscenium/flash",
|
6
|
+
"dependencies": {
|
7
|
+
"dom-mutations": "^1.0.0",
|
8
|
+
},
|
9
|
+
"devDependencies": {
|
10
|
+
"sourdough-toast": "latest",
|
11
|
+
},
|
12
|
+
},
|
13
|
+
},
|
14
|
+
"packages": {
|
15
|
+
"dom-mutations": ["dom-mutations@1.0.0", "", {}, "sha512-qr4ufk/qu+JKwtz7NPbu6TxpTM/7nqburohI07J+mKSM20USvhcUjEb8hWY6g2a3QYp3LtlGpi+mAZLPuxTi7g=="],
|
16
|
+
|
17
|
+
"sourdough-toast": ["sourdough-toast@0.1.0", "", {}, "sha512-ianhWqaaA5a0n9TRg6dLEt5DWflXqsQUMxEAGZi8JcHGqsPRm7XTWEfhhDKSi/zJ11CBbvh8b8oRcgxjCeYZeg=="],
|
18
|
+
}
|
19
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
@import "https://cdn.jsdelivr.net/npm/sourdough-toast/src/sourdough-toast.css";
|
@@ -0,0 +1,77 @@
|
|
1
|
+
import domMutations from "dom-mutations";
|
2
|
+
import { Sourdough, toast } from "sourdough-toast";
|
3
|
+
|
4
|
+
export function foo() {
|
5
|
+
console.log("foo");
|
6
|
+
}
|
7
|
+
|
8
|
+
class HueFlash extends HTMLElement {
|
9
|
+
static observedAttributes = ["data-flash-alert", "data-flash-notice"];
|
10
|
+
|
11
|
+
connectedCallback() {
|
12
|
+
this.#initSourdough();
|
13
|
+
}
|
14
|
+
|
15
|
+
async #initSourdough() {
|
16
|
+
if ("sourdoughBooted" in window) return;
|
17
|
+
|
18
|
+
const sourdough = new Sourdough({
|
19
|
+
richColors: true,
|
20
|
+
yPosition: "bottom",
|
21
|
+
xPosition: "center",
|
22
|
+
});
|
23
|
+
sourdough.boot();
|
24
|
+
window.sourdoughBooted = true;
|
25
|
+
|
26
|
+
// Watch for changes to htl:flashes meta tag
|
27
|
+
const flashesSelector = "meta[name='rails:flashes']";
|
28
|
+
for await (const mutation of domMutations(document.head, {
|
29
|
+
childList: true,
|
30
|
+
subtree: true,
|
31
|
+
attributes: true,
|
32
|
+
})) {
|
33
|
+
let $ele = null;
|
34
|
+
|
35
|
+
if (
|
36
|
+
mutation.type === "attributes" &&
|
37
|
+
mutation.target.nodeName == "META" &&
|
38
|
+
mutation.attributeName == "content"
|
39
|
+
) {
|
40
|
+
$ele = mutation.target;
|
41
|
+
} else if (mutation.type === "childList") {
|
42
|
+
for (const node of mutation.addedNodes) {
|
43
|
+
if (node.matches(flashesSelector)) {
|
44
|
+
$ele = node;
|
45
|
+
break;
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
if ($ele) {
|
51
|
+
const flashes = JSON.parse($ele.getAttribute("content"));
|
52
|
+
for (const [type, message] of Object.entries(flashes)) {
|
53
|
+
if (type === "alert") {
|
54
|
+
toast.error(message);
|
55
|
+
} else if (type === "notice") {
|
56
|
+
toast.success(message);
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
attributeChangedCallback(name, _oldValue, newValue) {
|
64
|
+
this.#initSourdough();
|
65
|
+
|
66
|
+
if (newValue === null) return;
|
67
|
+
|
68
|
+
if (name === "data-flash-alert") {
|
69
|
+
toast.warning(newValue);
|
70
|
+
} else if (name === "data-flash-notice") {
|
71
|
+
toast.success(newValue);
|
72
|
+
}
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
!customElements.get("pui-flash") &&
|
77
|
+
customElements.define("pui-flash", HueFlash);
|
@@ -0,0 +1,33 @@
|
|
1
|
+
export type Options = MutationObserverInit & {signal?: AbortSignal};
|
2
|
+
|
3
|
+
/**
|
4
|
+
@returns An async iterable that yields [`MutationRecord`](https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord) objects representing individual mutations.
|
5
|
+
|
6
|
+
@example
|
7
|
+
```
|
8
|
+
import domMutations from 'dom-mutations';
|
9
|
+
|
10
|
+
const target = document.querySelector('#unicorn');
|
11
|
+
|
12
|
+
for await (const mutation of domMutations(target, {childList: true})) {
|
13
|
+
console.log('Mutation:', mutation);
|
14
|
+
}
|
15
|
+
```
|
16
|
+
*/
|
17
|
+
export default function domMutations(target: Node, options?: Options): AsyncIterable<MutationRecord>;
|
18
|
+
|
19
|
+
/**
|
20
|
+
Similar to `domMutations()`, but yields batches of [`MutationRecord`](https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord) objects, each batch representing a group of mutations captured together. This method is less convenient, but can be useful in some cases when you need to handle mutations together as a group.
|
21
|
+
|
22
|
+
@example
|
23
|
+
```
|
24
|
+
import {batchedDomMutations} from 'dom-mutations';
|
25
|
+
|
26
|
+
const target = document.querySelector('#unicorn');
|
27
|
+
|
28
|
+
for await (const mutations of batchedDomMutations(target, {childList: true})) {
|
29
|
+
console.log('Batch of mutations:', mutations);
|
30
|
+
}
|
31
|
+
```
|
32
|
+
*/
|
33
|
+
export function batchedDomMutations(target: Node, options?: Options): AsyncIterable<MutationRecord[]>;
|
@@ -0,0 +1,44 @@
|
|
1
|
+
export default function domMutations(target, options = {}) {
|
2
|
+
return {
|
3
|
+
async * [Symbol.asyncIterator]() {
|
4
|
+
for await (const mutations of batchedDomMutations(target, options)) {
|
5
|
+
yield * mutations;
|
6
|
+
}
|
7
|
+
},
|
8
|
+
};
|
9
|
+
}
|
10
|
+
|
11
|
+
export function batchedDomMutations(target, {signal, ...options} = {}) {
|
12
|
+
return {
|
13
|
+
async * [Symbol.asyncIterator]() {
|
14
|
+
signal?.throwIfAborted();
|
15
|
+
|
16
|
+
let resolveMutations;
|
17
|
+
let rejectMutations;
|
18
|
+
|
19
|
+
const observer = new globalThis.MutationObserver(mutations => {
|
20
|
+
resolveMutations?.(mutations);
|
21
|
+
});
|
22
|
+
|
23
|
+
observer.observe(target, options);
|
24
|
+
|
25
|
+
signal?.addEventListener('abort', () => {
|
26
|
+
rejectMutations?.(signal.reason);
|
27
|
+
observer.disconnect();
|
28
|
+
}, {once: true});
|
29
|
+
|
30
|
+
try {
|
31
|
+
while (true) {
|
32
|
+
signal?.throwIfAborted();
|
33
|
+
|
34
|
+
yield await new Promise((resolve, reject) => { // eslint-disable-line no-await-in-loop
|
35
|
+
resolveMutations = resolve;
|
36
|
+
rejectMutations = reject;
|
37
|
+
});
|
38
|
+
}
|
39
|
+
} finally {
|
40
|
+
observer.disconnect();
|
41
|
+
}
|
42
|
+
},
|
43
|
+
};
|
44
|
+
}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,59 @@
|
|
1
|
+
{
|
2
|
+
"name": "dom-mutations",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"description": "Observe changes to the DOM using an async iterable — A nicer API for MutationObserver",
|
5
|
+
"license": "MIT",
|
6
|
+
"repository": "sindresorhus/dom-mutations",
|
7
|
+
"funding": "https://github.com/sponsors/sindresorhus",
|
8
|
+
"author": {
|
9
|
+
"name": "Sindre Sorhus",
|
10
|
+
"email": "sindresorhus@gmail.com",
|
11
|
+
"url": "https://sindresorhus.com"
|
12
|
+
},
|
13
|
+
"type": "module",
|
14
|
+
"exports": {
|
15
|
+
"types": "./index.d.ts",
|
16
|
+
"default": "./index.js"
|
17
|
+
},
|
18
|
+
"sideEffects": false,
|
19
|
+
"engines": {
|
20
|
+
"node": ">=18"
|
21
|
+
},
|
22
|
+
"scripts": {
|
23
|
+
"test": "xo && ava"
|
24
|
+
},
|
25
|
+
"files": [
|
26
|
+
"index.js",
|
27
|
+
"index.d.ts"
|
28
|
+
],
|
29
|
+
"keywords": [
|
30
|
+
"mutationobserver",
|
31
|
+
"mutation",
|
32
|
+
"mutations",
|
33
|
+
"observer",
|
34
|
+
"observe",
|
35
|
+
"dom",
|
36
|
+
"document",
|
37
|
+
"node",
|
38
|
+
"element",
|
39
|
+
"html",
|
40
|
+
"changes",
|
41
|
+
"asynciterable",
|
42
|
+
"asynciterator",
|
43
|
+
"iterable",
|
44
|
+
"iterator",
|
45
|
+
"generator",
|
46
|
+
"async",
|
47
|
+
"events",
|
48
|
+
"stream",
|
49
|
+
"loop"
|
50
|
+
],
|
51
|
+
"devDependencies": {
|
52
|
+
"ava": "^5.3.1",
|
53
|
+
"jsdom": "^24.1.1",
|
54
|
+
"xo": "^0.59.3"
|
55
|
+
},
|
56
|
+
"ava": {
|
57
|
+
"serial": true
|
58
|
+
}
|
59
|
+
}
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# dom-mutations
|
2
|
+
|
3
|
+
> Observe changes to the DOM using an async iterable — A nicer API for [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
|
4
|
+
|
5
|
+
This package only works in the browser.
|
6
|
+
|
7
|
+
## Install
|
8
|
+
|
9
|
+
```sh
|
10
|
+
npm install dom-mutations
|
11
|
+
```
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
```js
|
16
|
+
import domMutations from 'dom-mutations';
|
17
|
+
|
18
|
+
const target = document.querySelector('#unicorn');
|
19
|
+
|
20
|
+
for await (const mutation of domMutations(target, {childList: true})) {
|
21
|
+
console.log('Mutation:', mutation);
|
22
|
+
}
|
23
|
+
```
|
24
|
+
|
25
|
+
## API
|
26
|
+
|
27
|
+
### domMutations(target, options?) <sup>(default export)</sup>
|
28
|
+
|
29
|
+
Accepts the same arguments as [`MutationObserver#observe()`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/observe#parameters) with an additional optional [`signal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal) option to abort the observation. If the signal is triggered, the async iterable throws an [abort error](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort).
|
30
|
+
|
31
|
+
Returns an [`AsyncIterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols) that yields [`MutationRecord`](https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord) objects representing individual mutations.
|
32
|
+
|
33
|
+
### batchedDomMutations(target, options?) <sup>(named export)</sup>
|
34
|
+
|
35
|
+
Similar to `domMutations()`, but yields batches of [`MutationRecord`](https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord) objects, each batch representing a group of mutations captured together. This method is less convenient, but can be useful in some cases when you need to handle mutations together as a group.
|
36
|
+
|
37
|
+
```js
|
38
|
+
import {batchedDomMutations} from 'dom-mutations';
|
39
|
+
|
40
|
+
const target = document.querySelector('#unicorn');
|
41
|
+
|
42
|
+
for await (const mutations of batchedDomMutations(target, {childList: true})) {
|
43
|
+
console.log('Batch of mutations:', mutations);
|
44
|
+
}
|
45
|
+
```
|
46
|
+
|
47
|
+
## FAQ
|
48
|
+
|
49
|
+
### How do I stop the iteration?
|
50
|
+
|
51
|
+
Simply `return` or `break` in the loop body.
|
52
|
+
|
53
|
+
### How do I stop the iteration from the outside?
|
54
|
+
|
55
|
+
**Triggering the iterator to return**
|
56
|
+
|
57
|
+
```js
|
58
|
+
import domMutations from 'dom-mutations';
|
59
|
+
|
60
|
+
const target = document.querySelector('#unicorn');
|
61
|
+
|
62
|
+
const mutationIterator = domMutations(target, {childList: true})[Symbol.asyncIterator]();
|
63
|
+
|
64
|
+
(async () => {
|
65
|
+
for await (const mutation of mutationIterator) {
|
66
|
+
console.log('Mutation:', mutation);
|
67
|
+
}
|
68
|
+
})();
|
69
|
+
|
70
|
+
setTimeout(() => {
|
71
|
+
mutationIterator.return();
|
72
|
+
}, 10000);
|
73
|
+
```
|
74
|
+
|
75
|
+
**Using a variable**
|
76
|
+
|
77
|
+
This has the downside of not ending the iteration until the next mutation.
|
78
|
+
|
79
|
+
```js
|
80
|
+
import domMutations from 'dom-mutations';
|
81
|
+
|
82
|
+
const target = document.querySelector('#unicorn');
|
83
|
+
|
84
|
+
let shouldStop = false;
|
85
|
+
|
86
|
+
(async () => {
|
87
|
+
for await (const mutation of domMutations(target, {childList: true})) {
|
88
|
+
if (shouldStop) {
|
89
|
+
break;
|
90
|
+
}
|
91
|
+
|
92
|
+
console.log('Mutation:', mutation);
|
93
|
+
}
|
94
|
+
})();
|
95
|
+
|
96
|
+
setTimeout(() => {
|
97
|
+
shouldStop = true;
|
98
|
+
}, 10000);
|
99
|
+
```
|
100
|
+
|
101
|
+
**Using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)**
|
102
|
+
|
103
|
+
Unlike the above approaches, this will make the iterable throw an [abort error](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort).
|
104
|
+
|
105
|
+
```js
|
106
|
+
import domMutations from 'dom-mutations';
|
107
|
+
|
108
|
+
const target = document.querySelector('#unicorn');
|
109
|
+
const controller = new AbortController();
|
110
|
+
const {signal} = controller;
|
111
|
+
|
112
|
+
(async () => {
|
113
|
+
for await (const mutation of domMutations(target, {childList: true, signal})) {
|
114
|
+
console.log('Mutation:', mutation);
|
115
|
+
}
|
116
|
+
})();
|
117
|
+
|
118
|
+
setTimeout(() => {
|
119
|
+
controller.abort();
|
120
|
+
}, 10000);
|
121
|
+
```
|
122
|
+
|
123
|
+
## Related
|
124
|
+
|
125
|
+
- [request-animation-frames](https://github.com/sindresorhus/request-animation-frames) - Use `requestAnimationFrame` as an async iterable, in any JavaScript environment
|