├── .github
├── FUNDING.yml
├── enhanced-debugging.jpg
├── logo-dark.svg
└── logo-light.svg
├── src
├── action-options
│ ├── whenOutside.js
│ ├── withConfirm.js
│ ├── index.js
│ ├── throttled.js
│ └── withKey.js
├── index.js
└── enableDebug
│ ├── values.js
│ └── targets.js
├── package.json
├── LICENSE.txt
└── README.md
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [Rails-Designer]
2 |
--------------------------------------------------------------------------------
/.github/enhanced-debugging.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rails-Designer/stimulus-fx/HEAD/.github/enhanced-debugging.jpg
--------------------------------------------------------------------------------
/src/action-options/whenOutside.js:
--------------------------------------------------------------------------------
1 | export function whenOutside({ event, element }) {
2 | return !element.contains(event.target);
3 | }
4 |
--------------------------------------------------------------------------------
/src/action-options/withConfirm.js:
--------------------------------------------------------------------------------
1 | export function withConfirm({ element }) {
2 | return confirm(element.dataset.confirm || "Are you sure?");
3 | };
4 |
--------------------------------------------------------------------------------
/src/action-options/index.js:
--------------------------------------------------------------------------------
1 | export { throttled } from "./throttled";
2 | export { withConfirm } from "./withConfirm";
3 | export { withKey } from "./withKey";
4 | export { whenOutside } from "./whenOutside";
5 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import * as actions from "./action-options"
2 |
3 | export { actions };
4 |
5 | export function registerActionOptions(application) {
6 | Object.entries(actions).forEach(([name, method]) => {
7 | application.registerActionOption(name, method);
8 | });
9 | }
10 |
--------------------------------------------------------------------------------
/src/action-options/throttled.js:
--------------------------------------------------------------------------------
1 | const throttleTimers = new WeakMap();
2 |
3 | export function throttled({ element }, { wait = Number(element.dataset.throttledWait) || 1000 } = {}) {
4 | if (!throttleTimers.has(element)) {
5 | throttleTimers.set(element, 0);
6 | }
7 |
8 | const now = Date.now();
9 | const lastRun = throttleTimers.get(element);
10 |
11 | if (now - lastRun >= wait) {
12 | throttleTimers.set(element, now);
13 |
14 | return true;
15 | }
16 |
17 | return false;
18 | }
19 |
--------------------------------------------------------------------------------
/src/action-options/withKey.js:
--------------------------------------------------------------------------------
1 | export function withKey({ event, data }) {
2 | const keys = data?.key?.split(",").map(key => key.trim().toLowerCase()) || [];
3 |
4 | if (!keys.length) return false;
5 |
6 | const keyMap = {
7 | "ctrl": "ctrlKey",
8 | "control": "ctrlKey",
9 | "meta": "metaKey",
10 | "command": "metaKey",
11 | "cmd": "metaKey",
12 | "alt": "altKey",
13 | "option": "altKey",
14 | "shift": "shiftKey"
15 | };
16 |
17 | return keys.every(key => {
18 | return !!event[keyMap[key] || ""];
19 | });
20 | }
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "stimulus-fx",
3 | "description": "Collection of useful action options for Stimulus.js controllers",
4 | "author": "Rails Designer Devs",
5 | "version": "1.3.2",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/rails-designer/stimulus-fx.git"
9 | },
10 | "bugs": {
11 | "url": "https://github.com/rails-designer/stimulus-fx/issues"
12 | },
13 | "main": "src/index.js",
14 | "exports": {
15 | ".": "./src/index.js",
16 | "./actions": "./src/action-options/index.js",
17 | "./actions/*": "./src/action-options/*.js"
18 | },
19 | "license": "MIT",
20 | "homepage": "https://railsdesigner.com/stimulus-fx"
21 | }
22 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2024 Rails Designer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/enableDebug/values.js:
--------------------------------------------------------------------------------
1 | export function values(identifier, callbacks) {
2 | callbacks.connect.push(function() {
3 | logValues({ on: this.element, for: identifier });
4 | });
5 | }
6 |
7 | function logValues({ on: element, for: identifier }) {
8 | const values = allValues(element, identifier);
9 |
10 | if (Object.keys(values).length === 0) return;
11 |
12 | console.group("Values");
13 | console.table(values);
14 | console.groupEnd();
15 | }
16 |
17 | function allValues(element, identifier) {
18 | const prefix = `${identifier}-`;
19 | const dataPrefix = "data-";
20 | const valueSuffix = "-value";
21 |
22 | return Object.fromEntries(
23 | Array.from(element.attributes)
24 | .filter(attribute =>
25 | attribute.name.startsWith(dataPrefix) &&
26 | attribute.name.startsWith(prefix, dataPrefix.length) &&
27 | attribute.name.endsWith(valueSuffix)
28 | )
29 | .map(attribute => {
30 | const attributeName = attribute.name.slice(dataPrefix.length);
31 | const valueName = attributeName.slice(prefix.length, attributeName.length - valueSuffix.length);
32 |
33 | return [valueName, attribute.value];
34 | })
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/src/enableDebug/targets.js:
--------------------------------------------------------------------------------
1 | export function targets(identifier, callbacks) {
2 | callbacks.connect.push(function() {
3 | logTargets({ on: this.element, for: identifier });
4 | });
5 | }
6 |
7 | function logTargets({ on: element, for: identifier }) {
8 | const targets = allTargets(element, identifier);
9 |
10 | if (Object.keys(targets).length === 0) return;
11 |
12 | console.group("Targets");
13 | console.table(formatted(targets))
14 | console.groupEnd();
15 | }
16 |
17 | function allTargets(element, identifier) {
18 | const targets = {};
19 | const targetElements = element.querySelectorAll(`[data-${identifier}-target]`);
20 |
21 | targetElements.forEach(targetElement => {
22 | const targetNames = targetElement.getAttribute(`data-${identifier}-target`).split(" ");
23 |
24 | targetNames.forEach(targetName => {
25 | if (!targets[targetName]) targets[targetName] = [];
26 |
27 | targets[targetName].push(targetElement);
28 | });
29 | });
30 |
31 | return targets;
32 | }
33 |
34 | function formatted(targets) {
35 | return Object.fromEntries(
36 | Object.entries(targets).map(([name, elements]) => [
37 | name,
38 | { count: elements.length, elements }
39 | ])
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Stimulus Fx
2 |
3 | Collection of useful action options for Stimulus.js controllers. See this article to learn how [custom action options in Stimulus](https://railsdesigner.com/stimulus-custom-action-options/) work.
4 |
5 |
6 | **Sponsored By [Rails Designer](https://railsdesigner.com/)**
7 |
8 |
9 |
13 |