@radioactive-labs/plutonium 0.1.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/LICENSE.txt +21 -0
- package/README.md +91 -0
- package/package.json +47 -0
- package/src/css/plutonium.css +3 -0
- package/src/dist/css/plutonium.css +1 -0
- package/src/dist/js/plutonium.js +12417 -0
- package/src/dist/js/plutonium.js.map +7 -0
- package/src/dist/js/plutonium.min.js +39 -0
- package/src/dist/js/plutonium.min.js.map +7 -0
- package/src/js/controllers/color_mode_controller.js +41 -0
- package/src/js/controllers/form_controller.js +13 -0
- package/src/js/controllers/frame_navigator_controller.js +99 -0
- package/src/js/controllers/has_many_panel_controller.js +8 -0
- package/src/js/controllers/interactive_action_form_controller.js +13 -0
- package/src/js/controllers/nav_grid_menu_controller.js +8 -0
- package/src/js/controllers/nav_grid_menu_item_controller.js +8 -0
- package/src/js/controllers/nav_user_controller.js +8 -0
- package/src/js/controllers/nav_user_link_controller.js +8 -0
- package/src/js/controllers/nav_user_section_controller.js +8 -0
- package/src/js/controllers/nested_resource_form_fields_controller.js +64 -0
- package/src/js/controllers/register_controllers.js +45 -0
- package/src/js/controllers/resource_dismiss_controller.js +39 -0
- package/src/js/controllers/resource_drop_down_controller.js +31 -0
- package/src/js/controllers/resource_header_controller.js +8 -0
- package/src/js/controllers/resource_layout_controller.js +8 -0
- package/src/js/controllers/sidebar_menu_controller.js +8 -0
- package/src/js/controllers/sidebar_menu_item_controller.js +8 -0
- package/src/js/controllers/table_controller.js +8 -0
- package/src/js/controllers/table_search_input_controller.js +8 -0
- package/src/js/controllers/table_toolbar_controller.js +8 -0
- package/src/js/controllers/toolbar_controller.js +8 -0
- package/src/js/core.js +4 -0
- package/src/js/turbo/index.js +5 -0
- package/src/js/turbo/turbo_actions.js +8 -0
- package/src/js/turbo/turbo_debug.js +30 -0
- package/src/js/turbo/turbo_frame_monkey_patch.js +23 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
// Connects to data-controller="color-mode"
|
|
5
|
+
export default class extends Controller {
|
|
6
|
+
// static targets = ["trigger", "menu"]
|
|
7
|
+
|
|
8
|
+
connect() {
|
|
9
|
+
console.log(`color-mode connected: ${this.element}`)
|
|
10
|
+
this.updateColorMode()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
disconnect() {
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
updateColorMode() {
|
|
17
|
+
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
|
18
|
+
document.documentElement.classList.add('dark')
|
|
19
|
+
} else {
|
|
20
|
+
document.documentElement.classList.remove('dark')
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
setLightColorMode() {
|
|
25
|
+
// Whenever the user explicitly chooses light mode
|
|
26
|
+
localStorage.theme = 'light'
|
|
27
|
+
this.updateColorMode()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setDarkColorMode() {
|
|
31
|
+
// Whenever the user explicitly chooses dark mode
|
|
32
|
+
localStorage.theme = 'dark'
|
|
33
|
+
this.updateColorMode()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
setSystemColorMode() {
|
|
37
|
+
// Whenever the user explicitly chooses to respect the OS preference
|
|
38
|
+
localStorage.removeItem('theme')
|
|
39
|
+
this.updateColorMode()
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import debounce from "lodash.debounce";
|
|
3
|
+
|
|
4
|
+
// Connects to data-controller="form"
|
|
5
|
+
export default class extends Controller {
|
|
6
|
+
connect() {
|
|
7
|
+
console.log(`form connected: ${this.element}`)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
submit() {
|
|
11
|
+
this.element.requestSubmit()
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
// Connects to data-controller="frame-navigator"
|
|
4
|
+
export default class extends Controller {
|
|
5
|
+
static targets = ["frame", "refreshButton", "backButton", "homeButton"];
|
|
6
|
+
|
|
7
|
+
connect() {
|
|
8
|
+
console.log(`frame-navigator connected: ${this.element}`)
|
|
9
|
+
|
|
10
|
+
this.srcHistory = []
|
|
11
|
+
this.originalFrameSrc = this.frameTarget.src
|
|
12
|
+
|
|
13
|
+
if (this.hasRefreshButtonTarget) {
|
|
14
|
+
this.refreshButtonTarget.style.display = ''
|
|
15
|
+
this.refreshButtonClicked = this.refreshButtonClicked.bind(this);
|
|
16
|
+
this.refreshButtonTarget.addEventListener("click", this.refreshButtonClicked);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (this.hasBackButtonTarget) {
|
|
20
|
+
this.backButtonClicked = this.backButtonClicked.bind(this);
|
|
21
|
+
this.backButtonTarget.addEventListener("click", this.backButtonClicked);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (this.hasHomeButtonTarget) {
|
|
25
|
+
this.homeButtonClicked = this.homeButtonClicked.bind(this);
|
|
26
|
+
this.homeButtonTarget.addEventListener("click", this.homeButtonClicked);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.frameLoaded = this.frameLoaded.bind(this);
|
|
30
|
+
this.frameTarget.addEventListener("turbo:frame-load", this.frameLoaded);
|
|
31
|
+
|
|
32
|
+
this.frameLoading = this.frameLoading.bind(this);
|
|
33
|
+
this.frameTarget.addEventListener("turbo:click", this.frameLoading);
|
|
34
|
+
this.frameTarget.addEventListener("turbo:submit-start", this.frameLoading);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
disconnect() {
|
|
38
|
+
if (this.hasRefreshButtonTarget) this.refreshButtonTarget.removeEventListener("click", this.refreshButtonClicked);
|
|
39
|
+
if (this.hasBackButtonTarget) this.backButtonTarget.removeEventListener("click", this.backButtonClicked);
|
|
40
|
+
if (this.hasHomeButtonTarget) this.homeButtonTarget.removeEventListener("click", this.homeButtonClicked);
|
|
41
|
+
|
|
42
|
+
this.frameTarget.removeEventListener("turbo:frame-load", this.frameLoaded);
|
|
43
|
+
this.frameTarget.removeEventListener("turbo:click", this.frameLoading);
|
|
44
|
+
this.frameTarget.removeEventListener("turbo:submit-start", this.frameLoading);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
frameLoading(event) {
|
|
48
|
+
if (this.hasRefreshButtonTarget) this.refreshButtonTarget.classList.add("motion-safe:animate-spin")
|
|
49
|
+
this.frameTarget.classList.add("motion-safe:animate-pulse")
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
frameLoaded(event) {
|
|
53
|
+
if (this.hasRefreshButtonTarget) this.refreshButtonTarget.classList.remove("motion-safe:animate-spin")
|
|
54
|
+
this.frameTarget.classList.remove("motion-safe:animate-pulse")
|
|
55
|
+
|
|
56
|
+
let src = event.target.src
|
|
57
|
+
if (src == this.currentSrc) {
|
|
58
|
+
// this must be a refresh
|
|
59
|
+
// do nothing
|
|
60
|
+
}
|
|
61
|
+
else if (src == this.originalFrameSrc)
|
|
62
|
+
this.srcHistory = [src]
|
|
63
|
+
else
|
|
64
|
+
this.srcHistory.push(src)
|
|
65
|
+
|
|
66
|
+
this.updateNavigationButtonsDisplay()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
refreshButtonClicked(event) {
|
|
70
|
+
this.frameLoading(null)
|
|
71
|
+
|
|
72
|
+
this.frameTarget.reload()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
backButtonClicked(event) {
|
|
76
|
+
this.frameLoading(null)
|
|
77
|
+
|
|
78
|
+
this.srcHistory.pop()
|
|
79
|
+
this.frameTarget.src = this.currentSrc
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
homeButtonClicked(event) {
|
|
83
|
+
this.frameLoading(null)
|
|
84
|
+
|
|
85
|
+
this.frameTarget.src = this.originalFrameSrc
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
get currentSrc() { return this.srcHistory[this.srcHistory.length - 1] }
|
|
89
|
+
|
|
90
|
+
updateNavigationButtonsDisplay() {
|
|
91
|
+
if (this.hasHomeButtonTarget) {
|
|
92
|
+
this.homeButtonTarget.style.display = this.srcHistory.length > 1 ? '' : 'none'
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (this.hasBackButtonTarget) {
|
|
96
|
+
this.backButtonTarget.style.display = this.srcHistory.length > 2 ? '' : 'none'
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import debounce from "lodash.debounce";
|
|
3
|
+
|
|
4
|
+
// Connects to data-controller="interactive-action-form"
|
|
5
|
+
export default class extends Controller {
|
|
6
|
+
connect() {
|
|
7
|
+
console.log(`interactive-action-form connected: ${this.element}`)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
submit() {
|
|
11
|
+
this.element.requestSubmit()
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
// Connects to data-controller="nested-resource-form-fields"
|
|
4
|
+
// Copied from https://github.com/stimulus-components/stimulus-rails-nested-form/blob/master/src/index.ts
|
|
5
|
+
export default class extends Controller {
|
|
6
|
+
static targets = ["target", "template", "addButton"]
|
|
7
|
+
|
|
8
|
+
static values = {
|
|
9
|
+
wrapperSelector: {
|
|
10
|
+
type: String,
|
|
11
|
+
default: ".nested-resource-form-fields",
|
|
12
|
+
},
|
|
13
|
+
limit: Number,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
connect() {
|
|
17
|
+
this.updateState()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
add(e) {
|
|
21
|
+
e.preventDefault()
|
|
22
|
+
|
|
23
|
+
const content = this.templateTarget.innerHTML.replace(/NEW_RECORD/g, new Date().getTime().toString())
|
|
24
|
+
this.targetTarget.insertAdjacentHTML("beforebegin", content)
|
|
25
|
+
|
|
26
|
+
const event = new CustomEvent("nested-resource-form-fields:add", { bubbles: true })
|
|
27
|
+
this.element.dispatchEvent(event)
|
|
28
|
+
|
|
29
|
+
this.updateState()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
remove(e) {
|
|
33
|
+
e.preventDefault()
|
|
34
|
+
|
|
35
|
+
const wrapper = e.target.closest(this.wrapperSelectorValue)
|
|
36
|
+
|
|
37
|
+
if (wrapper.dataset.newRecord === "true") {
|
|
38
|
+
wrapper.remove()
|
|
39
|
+
} else {
|
|
40
|
+
wrapper.style.display = "none"
|
|
41
|
+
|
|
42
|
+
const input = wrapper.querySelector("input[name*='_destroy']")
|
|
43
|
+
input.value = "1"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const event = new CustomEvent("nested-resource-form-fields:remove", { bubbles: true })
|
|
47
|
+
this.element.dispatchEvent(event)
|
|
48
|
+
|
|
49
|
+
this.updateState()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
updateState() {
|
|
53
|
+
if (!this.hasAddButtonTarget || this.limitValue == 0) return
|
|
54
|
+
|
|
55
|
+
if (this.childCount >= this.limitValue)
|
|
56
|
+
this.addButtonTarget.style.display = "none"
|
|
57
|
+
else
|
|
58
|
+
this.addButtonTarget.style.display = "initial"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get childCount() {
|
|
62
|
+
return this.element.querySelectorAll(this.wrapperSelectorValue).length
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Import controllers here
|
|
2
|
+
import ResourceLayoutController from "./resource_layout_controller.js"
|
|
3
|
+
import NavGridMenuItemController from "./nav_grid_menu_item_controller.js"
|
|
4
|
+
import NavGridMenuController from "./nav_grid_menu_controller.js"
|
|
5
|
+
import NavUserSectionController from "./nav_user_section_controller.js"
|
|
6
|
+
import NavUserLinkController from "./nav_user_link_controller.js"
|
|
7
|
+
import NavUserController from "./nav_user_controller.js"
|
|
8
|
+
import ResourceHeaderController from "./resource_header_controller.js"
|
|
9
|
+
import SidebarMenuItemController from "./sidebar_menu_item_controller.js"
|
|
10
|
+
import SidebarMenuController from "./sidebar_menu_controller.js"
|
|
11
|
+
import HasManyPanelController from "./has_many_panel_controller.js"
|
|
12
|
+
import NestedResourceFormFieldsController from "./nested_resource_form_fields_controller.js"
|
|
13
|
+
import ToolbarController from "./toolbar_controller.js"
|
|
14
|
+
import TableSearchInputController from "./table_search_input_controller.js"
|
|
15
|
+
import TableToolbarController from "./table_toolbar_controller.js"
|
|
16
|
+
import TableController from "./table_controller.js"
|
|
17
|
+
import FormController from "./form_controller.js"
|
|
18
|
+
import ResourceDropDownController from "./resource_drop_down_controller.js"
|
|
19
|
+
import ResourceDismissController from "./resource_dismiss_controller.js"
|
|
20
|
+
import FrameNavigatorController from "./frame_navigator_controller.js"
|
|
21
|
+
import ColorModeController from "./color_mode_controller.js"
|
|
22
|
+
|
|
23
|
+
export default function (application) {
|
|
24
|
+
// Register controllers here
|
|
25
|
+
application.register("resource-layout", ResourceLayoutController)
|
|
26
|
+
application.register("nav-grid-menu-item", NavGridMenuItemController)
|
|
27
|
+
application.register("nav-grid-menu", NavGridMenuController)
|
|
28
|
+
application.register("nav-user-section", NavUserSectionController)
|
|
29
|
+
application.register("nav-user-link", NavUserLinkController)
|
|
30
|
+
application.register("nav-user", NavUserController)
|
|
31
|
+
application.register("resource-header", ResourceHeaderController)
|
|
32
|
+
application.register("sidebar-menu-item", SidebarMenuItemController)
|
|
33
|
+
application.register("sidebar-menu", SidebarMenuController)
|
|
34
|
+
application.register("has-many-panel", HasManyPanelController)
|
|
35
|
+
application.register("nested-resource-form-fields", NestedResourceFormFieldsController)
|
|
36
|
+
application.register("toolbar", ToolbarController)
|
|
37
|
+
application.register("table-search-input", TableSearchInputController)
|
|
38
|
+
application.register("table-toolbar", TableToolbarController)
|
|
39
|
+
application.register("table", TableController)
|
|
40
|
+
application.register("form", FormController)
|
|
41
|
+
application.register("resource-drop-down", ResourceDropDownController)
|
|
42
|
+
application.register("resource-dismiss", ResourceDismissController)
|
|
43
|
+
application.register("frame-navigator", FrameNavigatorController)
|
|
44
|
+
application.register("color-mode", ColorModeController)
|
|
45
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import { Dismiss } from 'flowbite';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
// Connects to data-controller="resource-dismiss"
|
|
6
|
+
export default class extends Controller {
|
|
7
|
+
static targets = ["trigger", "target"]
|
|
8
|
+
|
|
9
|
+
static values = {
|
|
10
|
+
after: Number,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
connect() {
|
|
14
|
+
console.log(`resource-dismiss connected: ${this.element}`)
|
|
15
|
+
|
|
16
|
+
// https://flowbite.com/docs/components/alerts/#javascript-behaviour
|
|
17
|
+
this.dismiss = new Dismiss(this.targetTarget, this.triggerTarget);
|
|
18
|
+
|
|
19
|
+
console.log(this.hasAfterValue)
|
|
20
|
+
console.log(this.afterValue)
|
|
21
|
+
if (this.hasAfterValue && this.afterValue > 0) {
|
|
22
|
+
this.autoDismissTimeout = setTimeout(() => {
|
|
23
|
+
this.hide()
|
|
24
|
+
this.autoDismissTimeout = null
|
|
25
|
+
}, this.afterValue);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
disconnect() {
|
|
30
|
+
if (this.autoDismissTimeout) clearTimeout(this.autoDismissTimeout)
|
|
31
|
+
|
|
32
|
+
this.dismiss = null
|
|
33
|
+
this.autoDismissTimeout = null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
hide() {
|
|
37
|
+
this.dismiss.hide()
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import { Dropdown } from 'flowbite';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
// Connects to data-controller="resource-drop-down"
|
|
6
|
+
export default class extends Controller {
|
|
7
|
+
static targets = ["trigger", "menu"]
|
|
8
|
+
|
|
9
|
+
connect() {
|
|
10
|
+
console.log(`resource-drop-down connected: ${this.element}`)
|
|
11
|
+
|
|
12
|
+
// https://flowbite.com/docs/components/dropdowns/#javascript-behaviour
|
|
13
|
+
this.dropdown = new Dropdown(this.menuTarget, this.triggerTarget);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
disconnect() {
|
|
17
|
+
this.dropdown = null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
toggle() {
|
|
21
|
+
this.dropdown.toggle()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
show() {
|
|
25
|
+
this.dropdown.show()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
hide() {
|
|
29
|
+
this.dropdown.show()
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/js/core.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
eventNames = [
|
|
2
|
+
'turbo:click',
|
|
3
|
+
'turbo:before-visit',
|
|
4
|
+
'turbo:visit',
|
|
5
|
+
'turbo:submit-start',
|
|
6
|
+
'turbo:before-fetch-request',
|
|
7
|
+
'turbo:before-fetch-response',
|
|
8
|
+
'turbo:submit-end',
|
|
9
|
+
'turbo:before-cache',
|
|
10
|
+
'turbo:before-render',
|
|
11
|
+
'turbo:before-stream-render',
|
|
12
|
+
'turbo:render',
|
|
13
|
+
'turbo:load',
|
|
14
|
+
'turbo:before-frame-render',
|
|
15
|
+
'turbo:frame-render',
|
|
16
|
+
'turbo:frame-load',
|
|
17
|
+
'turbo:frame-missing',
|
|
18
|
+
'turbo:fetch-request-error',
|
|
19
|
+
'turbo:reload', // https://github.com/hotwired/turbo/pull/556
|
|
20
|
+
'turbo:morph',
|
|
21
|
+
'turbo:before-morph-element',
|
|
22
|
+
'turbo:morph-attribute',
|
|
23
|
+
'turbo:morph',
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
eventNames.forEach(eventName => {
|
|
27
|
+
document.addEventListener(eventName, (event) => {
|
|
28
|
+
console.log(event.type, event);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Monkeypatch to fix turbo issue of wrong turbo-frame
|
|
2
|
+
// See: https://github.com/hotwired/turbo/pull/579
|
|
3
|
+
document.addEventListener("turbo:before-fetch-request", (event) => {
|
|
4
|
+
const targetTurboFrame = event.target.getAttribute("data-turbo-frame");
|
|
5
|
+
const fetchTurboFrame = event.detail.fetchOptions.headers["Turbo-Frame"];
|
|
6
|
+
if (
|
|
7
|
+
targetTurboFrame &&
|
|
8
|
+
targetTurboFrame != fetchTurboFrame &&
|
|
9
|
+
document.querySelector(`turbo-frame#${targetTurboFrame}`)
|
|
10
|
+
) {
|
|
11
|
+
event.detail.fetchOptions.headers["Turbo-Frame"] = targetTurboFrame;
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// // Reload the entire page if we are missing a frame
|
|
16
|
+
// // See: https://stackoverflow.com/a/75704489/644571
|
|
17
|
+
// document.addEventListener("turbo:frame-missing", (event) => {
|
|
18
|
+
// if (event.target.id != 'modal') return
|
|
19
|
+
|
|
20
|
+
// const { detail: { response, visit } } = event;
|
|
21
|
+
// event.preventDefault();
|
|
22
|
+
// visit(response);
|
|
23
|
+
// });
|