nitro_kit 0.4.0 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35f6533e2e09584b606c65bb7e19256d2573ebedc3d8dda8256304644a299fa3
4
- data.tar.gz: e39ef8b947407ea65eb6114c293444f07243b5fb09edd4dd8a7312b86eb110ea
3
+ metadata.gz: c6eec3cce7aa4fea915dc5db94356849f55d1335797a682651382287d9f63207
4
+ data.tar.gz: ea81efcdcd9d8b6e60a697f7d3b6e38ed26e6f6226f48a6ad249f9b984c6ae1b
5
5
  SHA512:
6
- metadata.gz: b17a7711886d99b9d89a13610ea457b910c222487fece000c874fdd9157f1fbe4dcac0ecd684f357174fc30c100a21ff6850f8f67cb1be1ff0c8773e6ad3a035
7
- data.tar.gz: 0c5f65acdb20d0bee4c2bd9484fe347c8853ec96f5e77be250ae3910d39bc1e5c44d9f034403974d34e6bddaf58abe23edc7c72cb59299950c1b25b5a38687d9
6
+ metadata.gz: 48a1d7b809cad9a30dae1e0d53210faea9c13e22b5c977fab4c3f4024a3fd7f7a4a30e50e69a449e817083a586388d162fc43d7ddf6a96a1159472b26eab0259
7
+ data.tar.gz: faecf1f62cd5e31fb729dd0c6ed80ff75245661d57adf09930e9b5a8de0b7f75f622776a391d6d8ebf22233a2a17375df02eb1bf459fdb0e47c6d0dc086f0c1f
@@ -0,0 +1,18 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ export default class extends Controller {
4
+ static targets = ["trigger", "content"];
5
+
6
+ toggle(event) {
7
+ const trigger = event.target;
8
+ const content = trigger.parentElement.querySelector(
9
+ "[data-nk--accordion-target=content]",
10
+ );
11
+
12
+ const isExpanded = trigger.getAttribute("aria-expanded") === "true";
13
+
14
+ // Toggle current item
15
+ trigger.setAttribute("aria-expanded", (!isExpanded).toString());
16
+ content.setAttribute("aria-hidden", isExpanded.toString());
17
+ }
18
+ }
@@ -0,0 +1,130 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+ import Combobox from "@github/combobox-nav";
3
+ import {
4
+ computePosition,
5
+ offset,
6
+ flip,
7
+ shift,
8
+ autoUpdate,
9
+ } from "@floating-ui/dom";
10
+
11
+ export default class extends Controller {
12
+ static targets = ["input", "list", "hiddenField", "clearButton"];
13
+ static values = {
14
+ open: { type: Boolean, default: false },
15
+ // Options for floating-ui
16
+ placement: { type: String },
17
+ // Options for combobox-nav
18
+ tabInsertsSuggestions: { type: Boolean },
19
+ firstOptionSelectionMode: { type: String },
20
+ scrollIntoViewOptions: { type: Object },
21
+ };
22
+
23
+ connect() {
24
+ this.combobox = new Combobox(this.inputTarget, this.listTarget, {
25
+ tabInsertsSuggestions: this.tabInsertsSuggestionsValue,
26
+ firstOptionSelectionMode: this.firstOptionSelectionModeValue,
27
+ scrollIntoViewOptions: this.scrollIntoViewOptionsValue,
28
+ });
29
+
30
+ this.updatePosition();
31
+
32
+ this.listTarget.addEventListener("combobox-commit", (event) => {
33
+ this.select(event);
34
+ this.close();
35
+ });
36
+ }
37
+
38
+ disconnect() {
39
+ this.combobox.destroy();
40
+ }
41
+
42
+ select(event) {
43
+ this.inputTarget.value = event.target.textContent;
44
+ this.hiddenFieldTarget.value =
45
+ event.target.dataset.value || event.target.textContent;
46
+ }
47
+
48
+ open() {
49
+ this.openValue = true;
50
+ }
51
+
52
+ close() {
53
+ this.openValue = false;
54
+ }
55
+
56
+ focusShift({ target }) {
57
+ if (!this.openValue) return;
58
+ if (this.element.contains(target)) return;
59
+
60
+ this.close();
61
+ }
62
+
63
+ windowClick({ target }) {
64
+ if (!this.openValue) return;
65
+ if (this.element.contains(target)) return;
66
+
67
+ this.close();
68
+ }
69
+
70
+ clear() {
71
+ this.combobox.resetSelection();
72
+ this.inputTarget.value = "";
73
+ this.input();
74
+ this.hiddenFieldTarget.value = "";
75
+ }
76
+
77
+ input(_event) {
78
+ if (!this.isOpen) this.open();
79
+
80
+ const filter = this.inputTarget.value.toLowerCase();
81
+
82
+ Array.from(this.listTarget.children).forEach((item) => {
83
+ const value = item.dataset.value?.toLowerCase();
84
+ const text = item.textContent.toLowerCase();
85
+
86
+ if (value?.includes(filter) || text.includes(filter)) {
87
+ item.setAttribute("role", "option");
88
+ } else {
89
+ item.removeAttribute("role");
90
+ }
91
+ });
92
+
93
+ this.hiddenFieldTarget.value = this.inputTarget.value;
94
+ }
95
+
96
+ openValueChanged(state, _previous) {
97
+ if (!this.combobox) return;
98
+
99
+ if (state) {
100
+ this.combobox.start();
101
+
102
+ this.listTarget.dataset.state = "open";
103
+
104
+ this.clearAutoUpdate = autoUpdate(
105
+ this.inputTarget,
106
+ this.listTarget,
107
+ this.updatePosition,
108
+ );
109
+ } else {
110
+ this.combobox.stop();
111
+
112
+ this.listTarget.dataset.state = "closed";
113
+
114
+ if (this.clearAutoUpdate) {
115
+ this.clearAutoUpdate();
116
+ }
117
+ }
118
+ }
119
+
120
+ updatePosition = () => {
121
+ computePosition(this.inputTarget, this.listTarget, {
122
+ placement: this.placementValue,
123
+ middleware: [offset(5), flip(), shift({ padding: 5 })],
124
+ }).then(({ x, y }) => {
125
+ this.listTarget.style.left = `${x}px`;
126
+ this.listTarget.style.top = `${y}px`;
127
+ this.listTarget.style.width = `${this.inputTarget.clientWidth}px`;
128
+ });
129
+ };
130
+ }
@@ -0,0 +1,5 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ export default class extends Controller {
4
+ static targets = ["input"];
5
+ }
@@ -0,0 +1,19 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ export default class extends Controller {
4
+ static targets = ["trigger", "dialog"];
5
+
6
+ open() {
7
+ this.dialogTarget.showModal();
8
+ }
9
+
10
+ close() {
11
+ this.dialogTarget.close();
12
+ }
13
+
14
+ clickOutside(event) {
15
+ if (event.target === this.dialogTarget) {
16
+ this.close();
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,78 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+ import {
3
+ computePosition,
4
+ offset,
5
+ flip,
6
+ shift,
7
+ autoUpdate,
8
+ } from "@floating-ui/dom";
9
+
10
+ export default class extends Controller {
11
+ static targets = ["trigger", "content"];
12
+ static values = {
13
+ placement: { type: String, default: "bottom" },
14
+ };
15
+
16
+ connect() {
17
+ this.updatePosition();
18
+ }
19
+
20
+ disconnect() {
21
+ this.close();
22
+ }
23
+
24
+ get isExpanded() {
25
+ return this.triggerTarget.getAttribute("aria-expanded") === "true";
26
+ }
27
+
28
+ updatePosition = () => {
29
+ computePosition(this.triggerTarget, this.contentTarget, {
30
+ placement: this.placementValue,
31
+ middleware: [offset(5), flip(), shift({ padding: 5 })],
32
+ }).then(({ x, y }) => {
33
+ this.contentTarget.style.left = `${x}px`;
34
+ this.contentTarget.style.top = `${y}px`;
35
+ });
36
+ };
37
+
38
+ open = () => {
39
+ this.triggerTarget.setAttribute("aria-expanded", "true");
40
+ this.contentTarget.setAttribute("aria-hidden", "false");
41
+
42
+ document.addEventListener("click", this.clickOutside);
43
+
44
+ this.clearAutoUpdate = autoUpdate(
45
+ this.triggerTarget,
46
+ this.contentTarget,
47
+ this.updatePosition,
48
+ );
49
+ };
50
+
51
+ close = () => {
52
+ this.triggerTarget.setAttribute("aria-expanded", "false");
53
+ this.contentTarget.setAttribute("aria-hidden", "true");
54
+
55
+ document.removeEventListener("click", this.clickOutside);
56
+
57
+ if (this.clearAutoUpdate) {
58
+ this.clearAutoUpdate();
59
+ }
60
+ };
61
+
62
+ toggle = () => {
63
+ if (this.isExpanded) {
64
+ this.close();
65
+ } else {
66
+ this.open();
67
+ }
68
+ };
69
+
70
+ clickOutside = (event) => {
71
+ if (
72
+ !this.contentTarget.contains(event.target) &&
73
+ !this.triggerTarget.contains(event.target)
74
+ ) {
75
+ this.close();
76
+ }
77
+ };
78
+ }
@@ -0,0 +1,32 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ export default class extends Controller {
4
+ connect() {
5
+ if (!this.element.hasAttribute("tabindex")) {
6
+ this.element.setAttribute("tabindex", "0");
7
+ }
8
+ }
9
+
10
+ click() {
11
+ if (this.element.hasAttribute("disabled")) return;
12
+
13
+ this.toggle();
14
+ }
15
+
16
+ keydown(event) {
17
+ if (this.element.hasAttribute("disabled")) return;
18
+
19
+ if (event.code === "Space" || event.code === "Enter") {
20
+ event.preventDefault();
21
+ this.toggle();
22
+ }
23
+ }
24
+
25
+ get checked() {
26
+ return this.element.getAttribute("aria-checked") === "true";
27
+ }
28
+
29
+ toggle() {
30
+ this.element.setAttribute("aria-checked", (!this.checked).toString());
31
+ }
32
+ }
@@ -0,0 +1,46 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ export default class extends Controller {
4
+ static targets = ["tab", "panel"];
5
+ static values = { active: String };
6
+
7
+ setActiveTab(event) {
8
+ this.activeValue = event.params.key;
9
+ }
10
+
11
+ prevTab(event) {
12
+ const prevTab = event.target.previousElementSibling;
13
+ if (prevTab) {
14
+ prevTab.click();
15
+ prevTab.focus();
16
+ }
17
+ }
18
+
19
+ nextTab(event) {
20
+ const nextTab = event.target.nextElementSibling;
21
+ if (nextTab) {
22
+ nextTab.click();
23
+ nextTab.focus();
24
+ }
25
+ }
26
+
27
+ activeValueChanged() {
28
+ const value = this.activeValue;
29
+
30
+ this.panelTargets.forEach((panel) => {
31
+ if (panel.dataset.key === value) {
32
+ panel.ariaHidden = false;
33
+ } else {
34
+ panel.ariaHidden = true;
35
+ }
36
+ });
37
+
38
+ this.tabTargets.forEach((tab) => {
39
+ if (tab.dataset.key === value) {
40
+ tab.ariaSelected = true;
41
+ } else {
42
+ tab.ariaSelected = false;
43
+ }
44
+ });
45
+ }
46
+ }
@@ -0,0 +1,68 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ export default class extends Controller {
4
+ static targets = ["list", "template", "sink"];
5
+ static values = {
6
+ duration: { type: Number, default: 5000 },
7
+ };
8
+
9
+ connect() {
10
+ this.#flushSink();
11
+
12
+ if (this.hasSinkTarget) {
13
+ this.mutationObserver = new MutationObserver(([event]) => {
14
+ if (event.addedNodes.length === 0) return;
15
+ this.#flushSink();
16
+ });
17
+ this.mutationObserver.observe(this.sinkTarget, { childList: true });
18
+ }
19
+ }
20
+
21
+ disconnect() {
22
+ if (this.mutationObserver) this.mutationObserver.disconnect();
23
+ }
24
+
25
+ toast({ params }) {
26
+ const { title, description } = params;
27
+ const item = this.templateTarget.content.cloneNode(true);
28
+
29
+ item.querySelector("[data-slot=title]").textContent = title;
30
+ item.querySelector("[data-slot=description]").textContent = description;
31
+
32
+ this.show(item);
33
+ }
34
+
35
+ show(item) {
36
+ this.clear();
37
+ this.listTarget.appendChild(item);
38
+
39
+ requestAnimationFrame(() => {
40
+ this.listTarget.children[0].dataset.state = "open";
41
+ });
42
+
43
+ if (this.timer) clearTimeout(this.timer);
44
+
45
+ this.timer = setTimeout(() => {
46
+ this.hide();
47
+ }, this.durationValue);
48
+ }
49
+
50
+ hide() {
51
+ this.listTarget.children[0].dataset.state = "closed";
52
+
53
+ setTimeout(() => {
54
+ this.clear();
55
+ }, 250);
56
+ }
57
+
58
+ clear() {
59
+ this.listTarget.innerHTML = "";
60
+ }
61
+
62
+ #flushSink() {
63
+ for (const li of this.sinkTarget.children) {
64
+ this.show(li.cloneNode(true));
65
+ li.remove();
66
+ }
67
+ }
68
+ }
@@ -0,0 +1,59 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+ import {
3
+ computePosition,
4
+ offset,
5
+ flip,
6
+ shift,
7
+ autoUpdate,
8
+ } from "@floating-ui/dom";
9
+
10
+ export default class extends Controller {
11
+ static targets = ["content"];
12
+ static values = {
13
+ open: { type: Boolean, default: false },
14
+ // Options for floating-ui
15
+ placement: { type: String, default: "top" },
16
+ };
17
+
18
+ connect() {
19
+ this.updatePosition();
20
+ }
21
+
22
+ disconnect() {
23
+ this.close();
24
+ }
25
+
26
+ open() {
27
+ this.openValue = true;
28
+ }
29
+
30
+ close() {
31
+ this.openValue = false;
32
+ }
33
+
34
+ openValueChanged(state, _previous) {
35
+ this.contentTarget.dataset.state = state ? "open" : "closed";
36
+
37
+ if (state) {
38
+ this.clearAutoUpdate = autoUpdate(
39
+ this.element,
40
+ this.contentTarget,
41
+ this.updatePosition,
42
+ );
43
+ } else {
44
+ if (this.clearAutoUpdate) {
45
+ this.clearAutoUpdate();
46
+ }
47
+ }
48
+ }
49
+
50
+ updatePosition = () => {
51
+ computePosition(this.element, this.contentTarget, {
52
+ placement: this.placementValue,
53
+ middleware: [offset(5), flip(), shift({ padding: 5 })],
54
+ }).then(({ x, y }) => {
55
+ this.contentTarget.style.left = `${x}px`;
56
+ this.contentTarget.style.top = `${y}px`;
57
+ });
58
+ };
59
+ }
@@ -1,5 +1,5 @@
1
1
  module NitroKit
2
- class AddGenerator < Rails::Generators::Base
2
+ class ComponentGenerator < Rails::Generators::Base
3
3
  argument :component_names, type: :array
4
4
 
5
5
  source_root File.expand_path("../../../", __dir__).tap { |path| puts path }
@@ -1,3 +1,3 @@
1
1
  module NitroKit
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nitro_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikkel Malmberg
@@ -120,8 +120,16 @@ files:
120
120
  - app/helpers/nitro_kit/textarea_helper.rb
121
121
  - app/helpers/nitro_kit/toast_helper.rb
122
122
  - app/helpers/nitro_kit/tooltip_helper.rb
123
- - lib/generators/nitro_kit/add_generator.rb
124
- - lib/generators/nitro_kit/install_generator.rb
123
+ - app/javascript/controllers/nk/accordion_controller.js
124
+ - app/javascript/controllers/nk/combobox_controller.js
125
+ - app/javascript/controllers/nk/datepicker_controller.js
126
+ - app/javascript/controllers/nk/dialog_controller.js
127
+ - app/javascript/controllers/nk/dropdown_controller.js
128
+ - app/javascript/controllers/nk/switch_controller.js
129
+ - app/javascript/controllers/nk/tabs_controller.js
130
+ - app/javascript/controllers/nk/toast_controller.js
131
+ - app/javascript/controllers/nk/tooltip_controller.js
132
+ - lib/generators/nitro_kit/component_generator.rb
125
133
  - lib/nitro_kit.rb
126
134
  - lib/nitro_kit/engine.rb
127
135
  - lib/nitro_kit/schema_builder.rb
@@ -1,10 +0,0 @@
1
- module NitroKit
2
- module Generators
3
- class InstallGenerator < Rails::Generators::Base
4
- def one
5
- puts("Maybe, maybe not. Seems complex. Maybe only support greenfield apps?")
6
- puts("Manual instructions: https://nitrokit.dev/getting_started")
7
- end
8
- end
9
- end
10
- end