app/assets/javascripts/stimulus_reflex.js in stimulus_reflex-3.5.0.pre10 vs app/assets/javascripts/stimulus_reflex.js in stimulus_reflex-3.5.0.rc1
- old
+ new
@@ -1,11 +1,291 @@
import { Controller } from "@hotwired/stimulus";
+import CableReady, { Utils } from "cable_ready";
+
import { createConsumer } from "@rails/actioncable";
-import CableReady, { Utils } from "cable_ready";
+/*!
+ * Toastify js 1.12.0
+ * https://github.com/apvarun/toastify-js
+ * @license MIT licensed
+ *
+ * Copyright (C) 2018 Varun A P
+ */ class Toastify {
+ defaults={
+ oldestFirst: true,
+ text: "Toastify is awesome!",
+ node: undefined,
+ duration: 3e3,
+ selector: undefined,
+ callback: function() {},
+ destination: undefined,
+ newWindow: false,
+ close: false,
+ gravity: "toastify-top",
+ positionLeft: false,
+ position: "",
+ backgroundColor: "",
+ avatar: "",
+ className: "",
+ stopOnFocus: true,
+ onClick: function() {},
+ offset: {
+ x: 0,
+ y: 0
+ },
+ escapeMarkup: true,
+ ariaLive: "polite",
+ style: {
+ background: ""
+ }
+ };
+ constructor(options) {
+ this.version = "1.12.0";
+ this.options = {};
+ this.toastElement = null;
+ this._rootElement = document.body;
+ this._init(options);
+ }
+ showToast() {
+ this.toastElement = this._buildToast();
+ if (typeof this.options.selector === "string") {
+ this._rootElement = document.getElementById(this.options.selector);
+ } else if (this.options.selector instanceof HTMLElement || this.options.selector instanceof ShadowRoot) {
+ this._rootElement = this.options.selector;
+ } else {
+ this._rootElement = document.body;
+ }
+ if (!this._rootElement) {
+ throw "Root element is not defined";
+ }
+ this._rootElement.insertBefore(this.toastElement, this._rootElement.firstChild);
+ this._reposition();
+ if (this.options.duration > 0) {
+ this.toastElement.timeOutValue = window.setTimeout((() => {
+ this._removeElement(this.toastElement);
+ }), this.options.duration);
+ }
+ return this;
+ }
+ hideToast() {
+ if (this.toastElement.timeOutValue) {
+ clearTimeout(this.toastElement.timeOutValue);
+ }
+ this._removeElement(this.toastElement);
+ }
+ _init(options) {
+ this.options = Object.assign(this.defaults, options);
+ if (this.options.backgroundColor) {
+ console.warn('DEPRECATION NOTICE: "backgroundColor" is being deprecated. Please use the "style.background" property.');
+ }
+ this.toastElement = null;
+ this.options.gravity = options.gravity === "bottom" ? "toastify-bottom" : "toastify-top";
+ this.options.stopOnFocus = options.stopOnFocus === undefined ? true : options.stopOnFocus;
+ if (options.backgroundColor) {
+ this.options.style.background = options.backgroundColor;
+ }
+ }
+ _buildToast() {
+ if (!this.options) {
+ throw "Toastify is not initialized";
+ }
+ let divElement = document.createElement("div");
+ divElement.className = `toastify on ${this.options.className}`;
+ divElement.className += ` toastify-${this.options.position}`;
+ divElement.className += ` ${this.options.gravity}`;
+ for (const property in this.options.style) {
+ divElement.style[property] = this.options.style[property];
+ }
+ if (this.options.ariaLive) {
+ divElement.setAttribute("aria-live", this.options.ariaLive);
+ }
+ if (this.options.node && this.options.node.nodeType === Node.ELEMENT_NODE) {
+ divElement.appendChild(this.options.node);
+ } else {
+ if (this.options.escapeMarkup) {
+ divElement.innerText = this.options.text;
+ } else {
+ divElement.innerHTML = this.options.text;
+ }
+ if (this.options.avatar !== "") {
+ let avatarElement = document.createElement("img");
+ avatarElement.src = this.options.avatar;
+ avatarElement.className = "toastify-avatar";
+ if (this.options.position == "left") {
+ divElement.appendChild(avatarElement);
+ } else {
+ divElement.insertAdjacentElement("afterbegin", avatarElement);
+ }
+ }
+ }
+ if (this.options.close === true) {
+ let closeElement = document.createElement("button");
+ closeElement.type = "button";
+ closeElement.setAttribute("aria-label", "Close");
+ closeElement.className = "toast-close";
+ closeElement.innerHTML = "✖";
+ closeElement.addEventListener("click", (event => {
+ event.stopPropagation();
+ this._removeElement(this.toastElement);
+ window.clearTimeout(this.toastElement.timeOutValue);
+ }));
+ const width = window.innerWidth > 0 ? window.innerWidth : screen.width;
+ if (this.options.position == "left" && width > 360) {
+ divElement.insertAdjacentElement("afterbegin", closeElement);
+ } else {
+ divElement.appendChild(closeElement);
+ }
+ }
+ if (this.options.stopOnFocus && this.options.duration > 0) {
+ divElement.addEventListener("mouseover", (event => {
+ window.clearTimeout(divElement.timeOutValue);
+ }));
+ divElement.addEventListener("mouseleave", (() => {
+ divElement.timeOutValue = window.setTimeout((() => {
+ this._removeElement(divElement);
+ }), this.options.duration);
+ }));
+ }
+ if (typeof this.options.destination !== "undefined") {
+ divElement.addEventListener("click", (event => {
+ event.stopPropagation();
+ if (this.options.newWindow === true) {
+ window.open(this.options.destination, "_blank");
+ } else {
+ window.location = this.options.destination;
+ }
+ }));
+ }
+ if (typeof this.options.onClick === "function" && typeof this.options.destination === "undefined") {
+ divElement.addEventListener("click", (event => {
+ event.stopPropagation();
+ this.options.onClick();
+ }));
+ }
+ if (typeof this.options.offset === "object") {
+ const x = this._getAxisOffsetAValue("x", this.options);
+ const y = this._getAxisOffsetAValue("y", this.options);
+ const xOffset = this.options.position == "left" ? x : `-${x}`;
+ const yOffset = this.options.gravity == "toastify-top" ? y : `-${y}`;
+ divElement.style.transform = `translate(${xOffset},${yOffset})`;
+ }
+ return divElement;
+ }
+ _removeElement(toastElement) {
+ toastElement.className = toastElement.className.replace(" on", "");
+ window.setTimeout((() => {
+ if (this.options.node && this.options.node.parentNode) {
+ this.options.node.parentNode.removeChild(this.options.node);
+ }
+ if (toastElement.parentNode) {
+ toastElement.parentNode.removeChild(toastElement);
+ }
+ this.options.callback.call(toastElement);
+ this._reposition();
+ }), 400);
+ }
+ _reposition() {
+ let topLeftOffsetSize = {
+ top: 15,
+ bottom: 15
+ };
+ let topRightOffsetSize = {
+ top: 15,
+ bottom: 15
+ };
+ let offsetSize = {
+ top: 15,
+ bottom: 15
+ };
+ let allToasts = this._rootElement.querySelectorAll(".toastify");
+ let classUsed;
+ for (let i = 0; i < allToasts.length; i++) {
+ if (allToasts[i].classList.contains("toastify-top") === true) {
+ classUsed = "toastify-top";
+ } else {
+ classUsed = "toastify-bottom";
+ }
+ let height = allToasts[i].offsetHeight;
+ classUsed = classUsed.substr(9, classUsed.length - 1);
+ let offset = 15;
+ let width = window.innerWidth > 0 ? window.innerWidth : screen.width;
+ if (width <= 360) {
+ allToasts[i].style[classUsed] = `${offsetSize[classUsed]}px`;
+ offsetSize[classUsed] += height + offset;
+ } else {
+ if (allToasts[i].classList.contains("toastify-left") === true) {
+ allToasts[i].style[classUsed] = `${topLeftOffsetSize[classUsed]}px`;
+ topLeftOffsetSize[classUsed] += height + offset;
+ } else {
+ allToasts[i].style[classUsed] = `${topRightOffsetSize[classUsed]}px`;
+ topRightOffsetSize[classUsed] += height + offset;
+ }
+ }
+ }
+ }
+ _getAxisOffsetAValue(axis, options) {
+ if (options.offset[axis]) {
+ if (isNaN(options.offset[axis])) {
+ return options.offset[axis];
+ } else {
+ return `${options.offset[axis]}px`;
+ }
+ }
+ return "0px";
+ }
+}
+function StartToastifyInstance(options) {
+ return new Toastify(options);
+}
+
+CableReady.operations.stimulusReflexVersionMismatch = operation => {
+ const levels = {
+ info: {},
+ success: {
+ background: "#198754",
+ color: "white"
+ },
+ warn: {
+ background: "#ffc107",
+ color: "black"
+ },
+ error: {
+ background: "#dc3545",
+ color: "white"
+ }
+ };
+ const defaults = {
+ selector: setupToastify(),
+ close: true,
+ duration: 30 * 1e3,
+ gravity: "bottom",
+ position: "right",
+ newWindow: true,
+ style: levels[operation.level || "info"]
+ };
+ StartToastifyInstance({
+ ...defaults,
+ ...operation
+ }).showToast();
+};
+
+function setupToastify() {
+ const id = "stimulus-reflex-toast-element";
+ let element = document.querySelector(`#${id}`);
+ if (!element) {
+ element = document.createElement("div");
+ element.id = id;
+ document.documentElement.appendChild(element);
+ const styles = document.createElement("style");
+ styles.innerHTML = `\n #${id} .toastify {\n padding: 12px 20px;\n color: #ffffff;\n display: inline-block;\n background: -webkit-linear-gradient(315deg, #73a5ff, #5477f5);\n background: linear-gradient(135deg, #73a5ff, #5477f5);\n position: fixed;\n opacity: 0;\n transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);\n border-radius: 2px;\n cursor: pointer;\n text-decoration: none;\n max-width: calc(50% - 20px);\n z-index: 2147483647;\n bottom: -150px;\n right: 15px;\n }\n\n #${id} .toastify.on {\n opacity: 1;\n }\n\n #${id} .toast-close {\n background: transparent;\n border: 0;\n color: white;\n cursor: pointer;\n font-family: inherit;\n font-size: 1em;\n opacity: 0.4;\n padding: 0 5px;\n }\n `;
+ document.head.appendChild(styles);
+ }
+ return element;
+}
+
let deprecationWarnings = true;
var Deprecate = {
get enabled() {
return deprecationWarnings;
@@ -168,10 +448,12 @@
element = element.parentElement ? element.parentElement.closest(`[${Schema.reflexRoot}]`) : null;
}
return list;
};
+const reflexNameToControllerIdentifier = reflexName => reflexName.replace(/([a-z0–9])([A-Z])/g, "$1-$2").replace(/(::)/g, "--").replace(/-reflex$/gi, "").toLowerCase();
+
const stages = [ "created", "before", "delivered", "queued", "after", "finalized", "success", "error", "halted", "forbidden" ];
let lastReflex;
const reflexes = new Proxy({}, {
@@ -654,11 +936,11 @@
return attrs;
};
var name = "stimulus_reflex";
-var version = "3.5.0-pre10";
+var version = "3.5.0-rc1";
var description = "Build reactive applications with the Rails tooling you already know and love.";
var keywords = [ "ruby", "rails", "websockets", "actioncable", "turbolinks", "reactive", "cable", "ujs", "ssr", "stimulus", "reflex", "stimulus_reflex", "dom", "morphdom" ];
@@ -691,36 +973,38 @@
format: "yarn run prettier-standard ./javascript/**/*.js rollup.config.mjs",
build: "yarn rollup -c",
"build:watch": "yarn rollup -wc",
watch: "yarn build:watch",
test: "web-test-runner javascript/test/**/*.test.js",
+ "test:watch": "yarn test --watch",
"docs:dev": "vitepress dev docs",
- "docs:build": "vitepress build docs",
+ "docs:build": "vitepress build docs && cp docs/_redirects docs/.vitepress/dist",
"docs:preview": "vitepress preview docs"
};
var peerDependencies = {
"@hotwired/stimulus": ">= 3.0"
};
var dependencies = {
- "@hotwired/stimulus": ">= 3.0, < 4",
- "@rails/actioncable": ">= 6.0, < 8",
- cable_ready: "5.0.0-pre10"
+ "@hotwired/stimulus": "^3",
+ "@rails/actioncable": "^6 || ^7",
+ cable_ready: "5.0.0-rc1"
};
var devDependencies = {
"@open-wc/testing": "^3.1.7",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-terser": "^0.4.0",
"@web/dev-server-esbuild": "^0.3.3",
"@web/dev-server-rollup": "^0.3.21",
- "@web/test-runner": "^0.15.0",
+ "@web/test-runner": "^0.15.1",
"prettier-standard": "^16.4.1",
- rollup: "^3.17.1",
- vitepress: "^1.0.0-alpha.47"
+ rollup: "^3.19.1",
+ "toastify-js": "^1.12.0",
+ vitepress: "^1.0.0-alpha.56"
};
var packageInfo = {
name: name,
version: version,
@@ -946,15 +1230,15 @@
error: reflex.error,
toString: () => reflex.error
})));
};
-const localReflexControllers = element => attributeValues(element.getAttribute(Schema.controller)).reduce(((memo, name) => {
- const controller = App.app.getControllerForElementAndIdentifier(element, name);
- if (controller && controller.StimulusReflex) memo.push(controller);
- return memo;
-}), []);
+const localReflexControllers = element => {
+ const potentialIdentifiers = attributeValues(element.getAttribute(Schema.controller));
+ const potentialControllers = potentialIdentifiers.map((identifier => App.app.getControllerForElementAndIdentifier(element, identifier)));
+ return potentialControllers.filter((controller => controller && controller.StimulusReflex));
+};
const allReflexControllers = element => {
let controllers = [];
while (element) {
controllers = controllers.concat(localReflexControllers(element));
@@ -963,33 +1247,38 @@
return controllers;
};
const findControllerByReflexName = (reflexName, controllers) => {
const controller = controllers.find((controller => {
- if (!controller.identifier) return;
- return extractReflexName(reflexName).replace(/([a-z0–9])([A-Z])/g, "$1-$2").replace(/(::)/g, "--").toLowerCase() === controller.identifier;
+ if (!controller || !controller.identifier) return;
+ const identifier = reflexNameToControllerIdentifier(extractReflexName(reflexName));
+ return identifier === controller.identifier;
}));
return controller || controllers[0];
};
const scanForReflexes = debounce((() => {
const reflexElements = document.querySelectorAll(`[${Schema.reflex}]`);
reflexElements.forEach((element => scanForReflexesOnElement(element)));
}), 20);
-const scanForReflexesOnElement = element => {
+const scanForReflexesOnElement = (element, controller = null) => {
const controllerAttribute = element.getAttribute(Schema.controller);
- const controllers = attributeValues(controllerAttribute);
+ const controllers = attributeValues(controllerAttribute).filter((controller => controller !== "stimulus-reflex"));
const reflexAttribute = element.getAttribute(Schema.reflex);
const reflexAttributeNames = attributeValues(reflexAttribute);
const actionAttribute = element.getAttribute(Schema.action);
const actions = attributeValues(actionAttribute).filter((action => !action.includes("#__perform")));
reflexAttributeNames.forEach((reflexName => {
- const controller = findControllerByReflexName(reflexName, allReflexControllers(element));
+ const potentialControllers = [ controller ].concat(allReflexControllers(element));
+ controller = findControllerByReflexName(reflexName, potentialControllers);
const controllerName = controller ? controller.identifier : "stimulus-reflex";
actions.push(`${reflexName.split("->")[0]}->${controllerName}#__perform`);
- controllers.push(controllerName);
+ const parentControllerElement = element.closest(`[data-controller~=${controllerName}]`);
+ if (!parentControllerElement) {
+ controllers.push(controllerName);
+ }
}));
const controllerValue = attributeValue(controllers);
const actionValue = attributeValue(actions);
let emitReadyEvent = false;
if (controllerValue && element.getAttribute(Schema.controller) != controllerValue) {
@@ -1112,10 +1401,10 @@
return Object.fromEntries(Object.entries(target[prop]).filter((([_, reflex]) => reflex.controller === this)));
}.bind(this)
});
}
});
- scanForReflexesOnElement(controller.element);
+ scanForReflexesOnElement(controller.element, controller);
emitEvent("stimulus-reflex:controller-registered", {
detail: {
controller: controller
}
});