diff --git a/AGENTS.md b/AGENTS.md index 672b0bb0a..541e519dc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -45,6 +45,63 @@ Priority: higher=first | Block binders (rv-if,rv-each-*) prevent child parsing | rv-text|textContent | rv-html|innerHTML | rv-value|two-way input | rv-if|conditional block | rv-show|display toggle rv-each-*|iterate arrays/objects | rv-on-*|event listeners | rv-class-*|toggle CSS class | rv-style-*|inline CSS rv-checked,rv-disabled,rv-enabled|form states | rv-template|dynamic HTML+rebind | rv-assign|merge obj into model +rv-add-class|add class string to element | rv-remove-class|remove class string from element + +### rv-class vs rv-add-class — NO rv-class BINDER EXISTS + +There is **no `rv-class` binder** in Riba. Only these class-related binders exist: +- `rv-class-*` (star binder) — toggles a single CSS class based on a boolean: `rv-class-active="item.active"` +- `rv-add-class` — adds class(es) from a string value while **preserving** static classes from the `class` attribute +- `rv-remove-class` — removes a class string + +If you write `rv-class="someClassString"`, it falls through to the **generic attribute binder** which does `setAttribute("class", value)` — this **overwrites** the entire `class` attribute, destroying all static Tailwind classes. + +```html + +
+ + +
+``` + +### rv-if/rv-unless inside rv-each — KNOWN BUG + +`rv-if`/`rv-unless` are block binders that remove/insert DOM nodes. Inside `rv-each-*` child views, they do not reactively update when the iterated object's properties change. The initial render works, but subsequent property mutations are ignored. + +The problem is that `rv-if` removes the element it's placed on. When the rv-each child view tries to update, the element is gone. + +**Two workarounds:** + +**1. Use `rv-show`/`rv-hide` (preferred for simple cases)** +```html + +
+ Active +
+ + +
+ Active +
+``` +`rv-unless` → use `rv-hide`. For toggling between two elements, use `rv-show` on one and `rv-hide` on the other. Also works: `rv-class-*` to toggle CSS classes. + +**2. Wrap in a child element (when rv-if is needed)** +```html + +
  • + Link +
  • + + +
  • +
    + Link +
    +
  • +``` + +Duplicate `rv-class` attributes on the same element are also invalid (HTML drops the second). Use a single computed class string or separate `rv-class-*` toggles. ## Formatters diff --git a/demos/accessibility-gamepad/package.json b/demos/accessibility-gamepad/package.json index efdfbf728..962ee050c 100644 --- a/demos/accessibility-gamepad/package.json +++ b/demos/accessibility-gamepad/package.json @@ -28,23 +28,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/accessibility": "workspace:^", diff --git a/demos/accessibility-keyboard/package.json b/demos/accessibility-keyboard/package.json index f5491398a..16360498f 100644 --- a/demos/accessibility-keyboard/package.json +++ b/demos/accessibility-keyboard/package.json @@ -28,23 +28,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/accessibility": "workspace:^", diff --git a/demos/bs5-accordion/package.json b/demos/bs5-accordion/package.json index 153b1d9f3..65c32a38d 100644 --- a/demos/bs5-accordion/package.json +++ b/demos/bs5-accordion/package.json @@ -31,23 +31,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/bs5-dropdown/package.json b/demos/bs5-dropdown/package.json index aeed2d26c..27cae4362 100644 --- a/demos/bs5-dropdown/package.json +++ b/demos/bs5-dropdown/package.json @@ -31,23 +31,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/bs5-form/package.json b/demos/bs5-form/package.json index 3646f6df8..ece9f38f8 100644 --- a/demos/bs5-form/package.json +++ b/demos/bs5-form/package.json @@ -31,23 +31,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/bs5-notifications/package.json b/demos/bs5-notifications/package.json index 74f17cfae..42d813a51 100644 --- a/demos/bs5-notifications/package.json +++ b/demos/bs5-notifications/package.json @@ -31,22 +31,22 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/bs5-notifications/src/index.html b/demos/bs5-notifications/src/index.html index 0d682b846..875170437 100644 --- a/demos/bs5-notifications/src/index.html +++ b/demos/bs5-notifications/src/index.html @@ -2,7 +2,7 @@ Bs5 Notifications Demo - + diff --git a/demos/bs5-notifications/src/ts/components/bs5-notifications-example/bs5-notifications-example.component.html b/demos/bs5-notifications/src/ts/components/bs5-notifications-example/bs5-notifications-example.component.html index d41b523f2..9a0300d51 100644 --- a/demos/bs5-notifications/src/ts/components/bs5-notifications-example/bs5-notifications-example.component.html +++ b/demos/bs5-notifications/src/ts/components/bs5-notifications-example/bs5-notifications-example.component.html @@ -49,7 +49,7 @@

    Toast from component

    - +
    @@ -171,7 +171,7 @@

    Other notifications

    - +
    diff --git a/demos/bs5-photoswipe/package.json b/demos/bs5-photoswipe/package.json index e12d36258..e91dd600d 100644 --- a/demos/bs5-photoswipe/package.json +++ b/demos/bs5-photoswipe/package.json @@ -33,23 +33,23 @@ "@ribajs/types": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/bs5-responsive-components/package.json b/demos/bs5-responsive-components/package.json index 2fdf02e64..6c3790836 100644 --- a/demos/bs5-responsive-components/package.json +++ b/demos/bs5-responsive-components/package.json @@ -31,23 +31,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/bs5-slider/package.json b/demos/bs5-slider/package.json index 655b5150f..77e747af5 100644 --- a/demos/bs5-slider/package.json +++ b/demos/bs5-slider/package.json @@ -30,22 +30,22 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/bs5-slider/src/index.html b/demos/bs5-slider/src/index.html index 3eab0ce14..d14590986 100644 --- a/demos/bs5-slider/src/index.html +++ b/demos/bs5-slider/src/index.html @@ -3,7 +3,7 @@ Bootstrap 5 Slider Demo - + diff --git a/demos/bs5-slideshow/package.json b/demos/bs5-slideshow/package.json index b82641bcb..92118fbae 100644 --- a/demos/bs5-slideshow/package.json +++ b/demos/bs5-slideshow/package.json @@ -31,22 +31,22 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/bs5-tagged-image/package.json b/demos/bs5-tagged-image/package.json index f4cb7328e..139ac9e9b 100644 --- a/demos/bs5-tagged-image/package.json +++ b/demos/bs5-tagged-image/package.json @@ -32,24 +32,24 @@ "@ribajs/types": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@types/node": "^24.12.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", + "@types/node": "^24.12.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/bs5-theme/package.json b/demos/bs5-theme/package.json index 5c16c6732..ad36c06f5 100644 --- a/demos/bs5-theme/package.json +++ b/demos/bs5-theme/package.json @@ -31,23 +31,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/bs5-toggle-button-sidebar/package.json b/demos/bs5-toggle-button-sidebar/package.json index 34e990574..d744a10d7 100644 --- a/demos/bs5-toggle-button-sidebar/package.json +++ b/demos/bs5-toggle-button-sidebar/package.json @@ -31,23 +31,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/content-slider/package.json b/demos/content-slider/package.json index 93c32727a..a3502c7b5 100644 --- a/demos/content-slider/package.json +++ b/demos/content-slider/package.json @@ -32,22 +32,22 @@ "@ribajs/types": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/core-components/package.json b/demos/core-components/package.json index a53d0cd24..1af83154b 100644 --- a/demos/core-components/package.json +++ b/demos/core-components/package.json @@ -26,23 +26,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/core": "workspace:^", diff --git a/demos/core-each-item/package.json b/demos/core-each-item/package.json index 120ae9189..60232974a 100644 --- a/demos/core-each-item/package.json +++ b/demos/core-each-item/package.json @@ -26,23 +26,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/core": "workspace:^", diff --git a/demos/empty-template/package.json b/demos/empty-template/package.json index 5a8fb8361..0001e774f 100644 --- a/demos/empty-template/package.json +++ b/demos/empty-template/package.json @@ -30,23 +30,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/core": "workspace:^", diff --git a/demos/extras-game-loop/package.json b/demos/extras-game-loop/package.json index 50a49e3c4..51f00e15a 100644 --- a/demos/extras-game-loop/package.json +++ b/demos/extras-game-loop/package.json @@ -27,19 +27,19 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "sass": "^1.99.0", "serve": "^14.2.6", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/core": "workspace:^", diff --git a/demos/extras-image-events/package.json b/demos/extras-image-events/package.json index 9ff1ad386..b9c8c4259 100644 --- a/demos/extras-image-events/package.json +++ b/demos/extras-image-events/package.json @@ -30,22 +30,22 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/extras-image-events/src/index.html b/demos/extras-image-events/src/index.html index ccba397f3..c688095bc 100644 --- a/demos/extras-image-events/src/index.html +++ b/demos/extras-image-events/src/index.html @@ -3,7 +3,7 @@ Extras Image Events Demo - + diff --git a/demos/extras-scroll-events/package.json b/demos/extras-scroll-events/package.json index 3037cf890..e4270d32d 100644 --- a/demos/extras-scroll-events/package.json +++ b/demos/extras-scroll-events/package.json @@ -27,18 +27,18 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", + "postcss-preset-env": "^11.2.1", "serve": "^14.2.6", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/extras-touch-events/package.json b/demos/extras-touch-events/package.json index cf92ab374..929c896a0 100644 --- a/demos/extras-touch-events/package.json +++ b/demos/extras-touch-events/package.json @@ -26,22 +26,22 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/core": "workspace:^", diff --git a/demos/fuse-search/package.json b/demos/fuse-search/package.json index 83dda8fb9..fb8370743 100644 --- a/demos/fuse-search/package.json +++ b/demos/fuse-search/package.json @@ -31,23 +31,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/i18n-static/package.json b/demos/i18n-static/package.json index 0125c64e1..924b429cc 100644 --- a/demos/i18n-static/package.json +++ b/demos/i18n-static/package.json @@ -24,23 +24,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", "bootstrap": "5.3.8", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/core": "workspace:^", diff --git a/demos/i18n-static/src/index.html b/demos/i18n-static/src/index.html index 0c4f28024..611125e9d 100644 --- a/demos/i18n-static/src/index.html +++ b/demos/i18n-static/src/index.html @@ -2,7 +2,7 @@ i18n-static - +
    diff --git a/demos/iconset/package.json b/demos/iconset/package.json index e1183da82..988bf4def 100644 --- a/demos/iconset/package.json +++ b/demos/iconset/package.json @@ -31,23 +31,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/jsx/package.json b/demos/jsx/package.json index d7e0e0e92..50ca78035 100644 --- a/demos/jsx/package.json +++ b/demos/jsx/package.json @@ -31,23 +31,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/leaflet-map/package.json b/demos/leaflet-map/package.json index b27d942ee..d47d8c00f 100644 --- a/demos/leaflet-map/package.json +++ b/demos/leaflet-map/package.json @@ -32,22 +32,22 @@ "@ribajs/types": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/core": "workspace:^", diff --git a/demos/lottie/package.json b/demos/lottie/package.json index 50e224d04..49201d63c 100644 --- a/demos/lottie/package.json +++ b/demos/lottie/package.json @@ -30,23 +30,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/core": "workspace:^", diff --git a/demos/luxon/package.json b/demos/luxon/package.json index 5e8344538..3abc7fce9 100644 --- a/demos/luxon/package.json +++ b/demos/luxon/package.json @@ -33,22 +33,22 @@ "@ribajs/types": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/moment/package.json b/demos/moment/package.json index aaf4bf4cc..f70db4876 100644 --- a/demos/moment/package.json +++ b/demos/moment/package.json @@ -33,22 +33,22 @@ "@ribajs/types": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/core": "workspace:^", diff --git a/demos/monaco-editor/package.json b/demos/monaco-editor/package.json index 36b314d59..134739bc0 100644 --- a/demos/monaco-editor/package.json +++ b/demos/monaco-editor/package.json @@ -32,17 +32,17 @@ "@ribajs/types": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/core": "workspace:^", diff --git a/demos/monaco-editor/src/index.html b/demos/monaco-editor/src/index.html index 32c335f8c..75cd8a935 100644 --- a/demos/monaco-editor/src/index.html +++ b/demos/monaco-editor/src/index.html @@ -2,7 +2,7 @@ Monaco Example - +
    diff --git a/demos/podcast-fixed-player/package.json b/demos/podcast-fixed-player/package.json index ee7655ba9..e2eb8f79f 100644 --- a/demos/podcast-fixed-player/package.json +++ b/demos/podcast-fixed-player/package.json @@ -30,23 +30,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/podcast/package.json b/demos/podcast/package.json index 68d1a53df..e4691b86e 100644 --- a/demos/podcast/package.json +++ b/demos/podcast/package.json @@ -30,23 +30,23 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/router-slide-transition/package.json b/demos/router-slide-transition/package.json index 00e0f7c3f..08622c81c 100644 --- a/demos/router-slide-transition/package.json +++ b/demos/router-slide-transition/package.json @@ -34,19 +34,19 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/types": "workspace:^", "@ribajs/vite-config": "workspace:^", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/core": "workspace:^", diff --git a/demos/router-view/package.json b/demos/router-view/package.json index 9bd14e636..811df567e 100644 --- a/demos/router-view/package.json +++ b/demos/router-view/package.json @@ -32,22 +32,22 @@ "@ribajs/types": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/rv-video/package.json b/demos/rv-video/package.json index aa490ab08..9a350f500 100644 --- a/demos/rv-video/package.json +++ b/demos/rv-video/package.json @@ -32,22 +32,22 @@ "@ribajs/types": "workspace:^", "@ribajs/vite-config": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "postcss-modules": "^6.0.1", - "postcss-preset-env": "^11.2.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "postcss-preset-env": "^11.2.1", + "prettier": "^3.8.2", + "sass": "^1.99.0", "serve": "^14.2.6", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2", - "vite": "^8.0.3" + "vite": "^8.0.8" }, "dependencies": { "@ribajs/bs5": "workspace:^", diff --git a/demos/tw-accordion/package.json b/demos/tw-accordion/package.json new file mode 100644 index 000000000..b696a19d8 --- /dev/null +++ b/demos/tw-accordion/package.json @@ -0,0 +1,45 @@ +{ + "name": "@ribajs/demo-tw-accordion", + "version": "2.0.0-rc.23", + "type": "module", + "engines": { + "node": ">=24.0.0" + }, + "description": "", + "main": "./src/ts/main.ts", + "module": "src/ts/main.ts", + "source": "src/ts/main.ts", + "private": true, + "scripts": { + "build": "vite build --mode production", + "build:dev": "vite build --mode development", + "start": "vite", + "start:dev": "vite", + "serve": "serve dist", + "clear": "rm -rf ./dist", + "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx,.cts,.mts --fix && tsc --noEmit", + "check": "tsc --noEmit" + }, + "files": [ + "src" + ], + "author": "", + "license": "MIT", + "devDependencies": { + "@ribajs/eslint-config": "workspace:^", + "@ribajs/tsconfig": "workspace:^", + "@ribajs/vite-config": "workspace:^", + "@tailwindcss/vite": "^4.2.2", + "eslint": "^10.2.0", + "serve": "^14.2.6", + "typescript": "6.0.2", + "vite": "^8.0.8" + }, + "dependencies": { + "@ribajs/core": "workspace:^", + "@ribajs/extras": "workspace:^", + "@ribajs/tw": "workspace:^", + "@ribajs/utils": "workspace:^", + "tailwindcss": "^4.2.2" + } +} diff --git a/demos/tw-accordion/src/css/main.css b/demos/tw-accordion/src/css/main.css new file mode 100644 index 000000000..eec2b775c --- /dev/null +++ b/demos/tw-accordion/src/css/main.css @@ -0,0 +1,4 @@ +@import "tailwindcss"; +@import "@ribajs/tw/src/css/utilities.css"; + +@custom-variant dark (&:where(.dark, .dark *)); diff --git a/demos/tw-accordion/src/index.html b/demos/tw-accordion/src/index.html new file mode 100644 index 000000000..9388f8261 --- /dev/null +++ b/demos/tw-accordion/src/index.html @@ -0,0 +1,15 @@ + + + + + + TW Accordion Demo + + + +
    + +
    + + + diff --git a/demos/tw-accordion/src/ts/accordion.module.ts b/demos/tw-accordion/src/ts/accordion.module.ts new file mode 100644 index 000000000..064033ae0 --- /dev/null +++ b/demos/tw-accordion/src/ts/accordion.module.ts @@ -0,0 +1,12 @@ +import { RibaModule } from "@ribajs/core"; +import * as components from "./components/index.js"; + +export const DemoModule: RibaModule = { + binders: {}, + components, + formatters: {}, + services: {}, + init() { + return this; + }, +}; diff --git a/demos/tw-accordion/src/ts/components/demo-accordion/demo-accordion.component.html b/demos/tw-accordion/src/ts/components/demo-accordion/demo-accordion.component.html new file mode 100644 index 000000000..8f04898b3 --- /dev/null +++ b/demos/tw-accordion/src/ts/components/demo-accordion/demo-accordion.component.html @@ -0,0 +1,60 @@ +

    TW Accordion & Collapse Demo

    + + +
    +

    Accordion

    +

    An accordion with three items. Only one item can be open at a time.

    + + + + + +
    + + +
    +

    Accordion (Multiple Open)

    +

    An accordion that allows multiple items to be open simultaneously.

    + + + + + +
    + + +
    +

    Standalone Collapse

    +

    A standalone collapsible section, independent of any accordion group.

    +
    + + +
    +
    + + +
    +

    Collapse with Rich Content

    +

    A collapse component can contain any HTML content.

    + +
      +
    • Smooth animated transitions
    • +
    • Keyboard accessible
    • +
    • ARIA attributes for screen readers
    • +
    • Works standalone or inside an accordion
    • +
    • Customizable via Tailwind classes
    • +
    +
    +
    diff --git a/demos/tw-accordion/src/ts/components/demo-accordion/demo-accordion.component.ts b/demos/tw-accordion/src/ts/components/demo-accordion/demo-accordion.component.ts new file mode 100644 index 000000000..f3916b342 --- /dev/null +++ b/demos/tw-accordion/src/ts/components/demo-accordion/demo-accordion.component.ts @@ -0,0 +1,36 @@ +import { Component } from "@ribajs/core"; +import { hasChildNodesTrim } from "@ribajs/utils/src/dom.js"; + +export class DemoAccordionComponent extends Component { + public static tagName = "demo-accordion"; + + protected autobind = true; + static get observedAttributes(): string[] { + return []; + } + + public scope = {}; + + constructor() { + super(); + } + + protected connectedCallback() { + super.connectedCallback(); + super.init(DemoAccordionComponent.observedAttributes); + } + + protected requiredAttributes(): string[] { + return []; + } + + protected async template() { + if (hasChildNodesTrim(this)) { + return null; + } else { + const { default: template } = + await import("./demo-accordion.component.html?raw"); + return template; + } + } +} diff --git a/demos/tw-accordion/src/ts/components/index.ts b/demos/tw-accordion/src/ts/components/index.ts new file mode 100644 index 000000000..86df93226 --- /dev/null +++ b/demos/tw-accordion/src/ts/components/index.ts @@ -0,0 +1 @@ +export { DemoAccordionComponent } from "./demo-accordion/demo-accordion.component.js"; diff --git a/demos/tw-accordion/src/ts/main.ts b/demos/tw-accordion/src/ts/main.ts new file mode 100644 index 000000000..a953941c5 --- /dev/null +++ b/demos/tw-accordion/src/ts/main.ts @@ -0,0 +1,14 @@ +import { coreModule, Riba } from "@ribajs/core"; +import { extrasModule } from "@ribajs/extras"; +import { twModule } from "@ribajs/tw"; +import { DemoModule } from "./accordion.module.js"; + +const riba = new Riba(); +const model = {}; + +riba.module.register(coreModule.init()); +riba.module.register(extrasModule.init()); +riba.module.register(twModule.init()); +riba.module.register(DemoModule.init()); + +riba.bind(document.body, model); diff --git a/demos/tw-accordion/tsconfig.json b/demos/tw-accordion/tsconfig.json new file mode 100644 index 000000000..df31a4647 --- /dev/null +++ b/demos/tw-accordion/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@ribajs/tsconfig/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": [] + }, + "exclude": [ + "node_modules", + "dist/**/*.d.ts" + ] +} diff --git a/demos/tw-accordion/vite.config.js b/demos/tw-accordion/vite.config.js new file mode 100644 index 000000000..136a53c10 --- /dev/null +++ b/demos/tw-accordion/vite.config.js @@ -0,0 +1,6 @@ +import tailwindcss from "@tailwindcss/vite"; +import { ribaViteConfig } from "@ribajs/vite-config"; + +const config = ribaViteConfig(); +config.plugins = [...(config.plugins || []), tailwindcss()]; +export default config; diff --git a/demos/tw-basics/package.json b/demos/tw-basics/package.json new file mode 100644 index 000000000..dd5c01975 --- /dev/null +++ b/demos/tw-basics/package.json @@ -0,0 +1,45 @@ +{ + "name": "@ribajs/demo-tw-basics", + "version": "2.0.0-rc.23", + "type": "module", + "engines": { + "node": ">=24.0.0" + }, + "description": "", + "main": "./src/ts/main.ts", + "module": "src/ts/main.ts", + "source": "src/ts/main.ts", + "private": true, + "scripts": { + "build": "vite build --mode production", + "build:dev": "vite build --mode development", + "start": "vite", + "start:dev": "vite", + "serve": "serve dist", + "clear": "rm -rf ./dist", + "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx,.cts,.mts --fix && tsc --noEmit", + "check": "tsc --noEmit" + }, + "files": [ + "src" + ], + "author": "", + "license": "MIT", + "devDependencies": { + "@ribajs/eslint-config": "workspace:^", + "@ribajs/tsconfig": "workspace:^", + "@ribajs/vite-config": "workspace:^", + "@tailwindcss/vite": "^4.2.2", + "eslint": "^10.2.0", + "serve": "^14.2.6", + "typescript": "6.0.2", + "vite": "^8.0.8" + }, + "dependencies": { + "@ribajs/core": "workspace:^", + "@ribajs/extras": "workspace:^", + "@ribajs/tw": "workspace:^", + "@ribajs/utils": "workspace:^", + "tailwindcss": "^4.2.2" + } +} diff --git a/demos/tw-basics/src/css/main.css b/demos/tw-basics/src/css/main.css new file mode 100644 index 000000000..eec2b775c --- /dev/null +++ b/demos/tw-basics/src/css/main.css @@ -0,0 +1,4 @@ +@import "tailwindcss"; +@import "@ribajs/tw/src/css/utilities.css"; + +@custom-variant dark (&:where(.dark, .dark *)); diff --git a/demos/tw-basics/src/index.html b/demos/tw-basics/src/index.html new file mode 100644 index 000000000..d57d5ca64 --- /dev/null +++ b/demos/tw-basics/src/index.html @@ -0,0 +1,15 @@ + + + + + + Tailwind Basics Demo + + + +
    + +
    + + + diff --git a/demos/tw-basics/src/ts/basics.module.ts b/demos/tw-basics/src/ts/basics.module.ts new file mode 100644 index 000000000..064033ae0 --- /dev/null +++ b/demos/tw-basics/src/ts/basics.module.ts @@ -0,0 +1,12 @@ +import { RibaModule } from "@ribajs/core"; +import * as components from "./components/index.js"; + +export const DemoModule: RibaModule = { + binders: {}, + components, + formatters: {}, + services: {}, + init() { + return this; + }, +}; diff --git a/demos/tw-basics/src/ts/components/demo-basics/demo-basics.component.html b/demos/tw-basics/src/ts/components/demo-basics/demo-basics.component.html new file mode 100644 index 000000000..4fc26b545 --- /dev/null +++ b/demos/tw-basics/src/ts/components/demo-basics/demo-basics.component.html @@ -0,0 +1,328 @@ +

    Tailwind Basics Demo

    + + +
    +

    Alerts

    +
    + + + + +
    +
    + + +
    +

    Badges

    +
    + Default + Info + Success + Warning + Error +
    +

    Outline Badges

    +
    + Default + Info + Success + Warning + Error +
    +

    Badge Sizes

    +
    + Small + Medium + Large +
    +
    + + +
    +

    Avatars

    +

    Sizes

    +
    + + + + + +
    +

    With Status

    +
    + + + + +
    +

    With Image

    +
    + + + +
    +
    + + +
    +

    Cards

    +
    + +

    This is a basic card with an image and some body text content.

    +
    + +

    A compact card without an image. Useful for dense layouts.

    +
    + +

    Cards can contain any content, including other components.

    +
    +
    +
    + + +
    +

    Keyboard Keys

    +
    +

    + Press Ctrl + C to copy. +

    +

    + Press Ctrl + Shift + I to open DevTools. +

    +

    + Press Enter to submit the form. +

    +
    +
    + + +
    +

    Skeleton Loading

    +
    +
    +

    Text Lines

    + +
    +
    +

    Circle

    + +
    +
    +

    Rectangle

    + +
    +
    +
    +

    Card Skeleton

    + +
    +
    + + +
    +

    Progress Bars

    +
    +
    +

    Default (25%)

    + +
    +
    +

    Success (50%)

    + +
    +
    +

    Warning (75%)

    + +
    +
    +

    Error (90%)

    + +
    +

    Sizes

    +
    +

    Extra Small

    + +
    +
    +

    Small

    + +
    +
    +

    Medium

    + +
    +
    +

    Large

    + +
    +
    +
    + + +
    +

    Rating

    +
    +
    +

    Interactive (click to rate)

    + +
    +
    +

    Read-only (4.5 out of 5)

    + +
    +
    +

    Small rating

    + +
    +
    +

    Extra large rating (out of 10)

    + +
    +
    +
    + + +
    +

    Breadcrumbs

    +
    +
    +

    Default separator

    + + + + + + +
    +
    +

    Custom separator

    + + + + + +
    +
    +
    + + +
    +

    Animated Buttons

    +

    Click a button to see the animation.

    +
    + + + + + + + + + +
    +
    + + +
    +

    Tooltips

    +
    + + + + + + + + + + + + +
    +
    + + +
    +

    Swap Animations

    +

    Click to toggle between two states with different animations.

    +
    +
    +

    Rotate

    + +
    ☀️
    +
    🌙
    +
    +
    +
    +

    Flip

    + +
    👍
    +
    👎
    +
    +
    +
    +

    Fade

    + +
    🔔
    +
    🔕
    +
    +
    +
    +
    + + +
    +

    Pagination

    +
    +
    +

    10 pages, max 5 visible

    + +
    +
    +

    20 pages, starting on page 8

    + +
    +
    +
    + + +
    +

    Color Picker

    +
    + + + +
    +
    + + +
    +

    Collapse

    +

    Standalone collapsible sections (independent from Accordion).

    +
    + + +
    + This content is collapsible. It can contain any HTML including other components, images, or forms. +
    +
    + + +
    + Each collapse section operates independently. Opening one does not close the others (unlike an accordion). +
    +
    +
    +
    diff --git a/demos/tw-basics/src/ts/components/demo-basics/demo-basics.component.ts b/demos/tw-basics/src/ts/components/demo-basics/demo-basics.component.ts new file mode 100644 index 000000000..d7d4f0eed --- /dev/null +++ b/demos/tw-basics/src/ts/components/demo-basics/demo-basics.component.ts @@ -0,0 +1,36 @@ +import { Component } from "@ribajs/core"; +import { hasChildNodesTrim } from "@ribajs/utils/src/dom.js"; + +export class DemoBasicsComponent extends Component { + public static tagName = "demo-basics"; + + protected autobind = true; + static get observedAttributes(): string[] { + return []; + } + + public scope = {}; + + constructor() { + super(); + } + + protected connectedCallback() { + super.connectedCallback(); + super.init(DemoBasicsComponent.observedAttributes); + } + + protected requiredAttributes(): string[] { + return []; + } + + protected async template() { + if (hasChildNodesTrim(this)) { + return null; + } else { + const { default: template } = + await import("./demo-basics.component.html?raw"); + return template; + } + } +} diff --git a/demos/tw-basics/src/ts/components/index.ts b/demos/tw-basics/src/ts/components/index.ts new file mode 100644 index 000000000..12d9b2cef --- /dev/null +++ b/demos/tw-basics/src/ts/components/index.ts @@ -0,0 +1 @@ +export { DemoBasicsComponent } from "./demo-basics/demo-basics.component.js"; diff --git a/demos/tw-basics/src/ts/main.ts b/demos/tw-basics/src/ts/main.ts new file mode 100644 index 000000000..30eb87dc3 --- /dev/null +++ b/demos/tw-basics/src/ts/main.ts @@ -0,0 +1,14 @@ +import { coreModule, Riba } from "@ribajs/core"; +import { extrasModule } from "@ribajs/extras"; +import { twModule } from "@ribajs/tw"; +import { DemoModule } from "./basics.module.js"; + +const riba = new Riba(); +const model = {}; + +riba.module.register(coreModule.init()); +riba.module.register(extrasModule.init()); +riba.module.register(twModule.init()); +riba.module.register(DemoModule.init()); + +riba.bind(document.body, model); diff --git a/demos/tw-basics/tsconfig.json b/demos/tw-basics/tsconfig.json new file mode 100644 index 000000000..df31a4647 --- /dev/null +++ b/demos/tw-basics/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@ribajs/tsconfig/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": [] + }, + "exclude": [ + "node_modules", + "dist/**/*.d.ts" + ] +} diff --git a/demos/tw-basics/vite.config.js b/demos/tw-basics/vite.config.js new file mode 100644 index 000000000..136a53c10 --- /dev/null +++ b/demos/tw-basics/vite.config.js @@ -0,0 +1,6 @@ +import tailwindcss from "@tailwindcss/vite"; +import { ribaViteConfig } from "@ribajs/vite-config"; + +const config = ribaViteConfig(); +config.plugins = [...(config.plugins || []), tailwindcss()]; +export default config; diff --git a/demos/tw-dropdown/package.json b/demos/tw-dropdown/package.json new file mode 100644 index 000000000..97e2a2c2b --- /dev/null +++ b/demos/tw-dropdown/package.json @@ -0,0 +1,45 @@ +{ + "name": "@ribajs/demo-tw-dropdown", + "version": "2.0.0-rc.23", + "type": "module", + "engines": { + "node": ">=24.0.0" + }, + "description": "", + "main": "./src/ts/main.ts", + "module": "src/ts/main.ts", + "source": "src/ts/main.ts", + "private": true, + "scripts": { + "build": "vite build --mode production", + "build:dev": "vite build --mode development", + "start": "vite", + "start:dev": "vite", + "serve": "serve dist", + "clear": "rm -rf ./dist", + "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx,.cts,.mts --fix && tsc --noEmit", + "check": "tsc --noEmit" + }, + "files": [ + "src" + ], + "author": "", + "license": "MIT", + "devDependencies": { + "@ribajs/eslint-config": "workspace:^", + "@ribajs/tsconfig": "workspace:^", + "@ribajs/vite-config": "workspace:^", + "@tailwindcss/vite": "^4.2.2", + "eslint": "^10.2.0", + "serve": "^14.2.6", + "typescript": "6.0.2", + "vite": "^8.0.8" + }, + "dependencies": { + "@ribajs/core": "workspace:^", + "@ribajs/extras": "workspace:^", + "@ribajs/tw": "workspace:^", + "@ribajs/utils": "workspace:^", + "tailwindcss": "^4.2.2" + } +} diff --git a/demos/tw-dropdown/src/css/main.css b/demos/tw-dropdown/src/css/main.css new file mode 100644 index 000000000..eec2b775c --- /dev/null +++ b/demos/tw-dropdown/src/css/main.css @@ -0,0 +1,4 @@ +@import "tailwindcss"; +@import "@ribajs/tw/src/css/utilities.css"; + +@custom-variant dark (&:where(.dark, .dark *)); diff --git a/demos/tw-dropdown/src/index.html b/demos/tw-dropdown/src/index.html new file mode 100644 index 000000000..8eb5d45e1 --- /dev/null +++ b/demos/tw-dropdown/src/index.html @@ -0,0 +1,15 @@ + + + + + + TW Dropdown Demo + + + +
    + +
    + + + diff --git a/demos/tw-dropdown/src/ts/components/demo-dropdown/demo-dropdown.component.html b/demos/tw-dropdown/src/ts/components/demo-dropdown/demo-dropdown.component.html new file mode 100644 index 000000000..e3fc2b5b7 --- /dev/null +++ b/demos/tw-dropdown/src/ts/components/demo-dropdown/demo-dropdown.component.html @@ -0,0 +1,201 @@ +

    TW Dropdown & Navbar Demo

    + + +
    +

    Navbar

    +

    A responsive navbar with collapsible mobile menu.

    + + + +
    + + +
    +

    Basic Dropdown

    +

    A simple dropdown menu with default bottom-start placement.

    + + + + +
    + + +
    +

    Dropdown Placements

    +

    Dropdowns can be positioned in different directions using the placement attribute.

    +
    + + + + + + + + + + + + + + + + + + + +
    +
    + + +
    +

    Dropdown with Keyboard Navigation

    +

    This dropdown supports keyboard navigation. Click the button then use Arrow Down / Arrow Up to navigate, Enter to select, and Escape to close.

    + + + + +
    + + +
    +

    Styled Dropdown with Icons

    +

    A dropdown with icon-decorated menu items.

    + + + + +
    diff --git a/demos/tw-dropdown/src/ts/components/demo-dropdown/demo-dropdown.component.ts b/demos/tw-dropdown/src/ts/components/demo-dropdown/demo-dropdown.component.ts new file mode 100644 index 000000000..1318618c9 --- /dev/null +++ b/demos/tw-dropdown/src/ts/components/demo-dropdown/demo-dropdown.component.ts @@ -0,0 +1,36 @@ +import { Component } from "@ribajs/core"; +import { hasChildNodesTrim } from "@ribajs/utils/src/dom.js"; + +export class DemoDropdownComponent extends Component { + public static tagName = "demo-dropdown"; + + protected autobind = true; + static get observedAttributes(): string[] { + return []; + } + + public scope = {}; + + constructor() { + super(); + } + + protected connectedCallback() { + super.connectedCallback(); + super.init(DemoDropdownComponent.observedAttributes); + } + + protected requiredAttributes(): string[] { + return []; + } + + protected async template() { + if (hasChildNodesTrim(this)) { + return null; + } else { + const { default: template } = + await import("./demo-dropdown.component.html?raw"); + return template; + } + } +} diff --git a/demos/tw-dropdown/src/ts/components/index.ts b/demos/tw-dropdown/src/ts/components/index.ts new file mode 100644 index 000000000..d1606563a --- /dev/null +++ b/demos/tw-dropdown/src/ts/components/index.ts @@ -0,0 +1 @@ +export { DemoDropdownComponent } from "./demo-dropdown/demo-dropdown.component.js"; diff --git a/demos/tw-dropdown/src/ts/dropdown.module.ts b/demos/tw-dropdown/src/ts/dropdown.module.ts new file mode 100644 index 000000000..064033ae0 --- /dev/null +++ b/demos/tw-dropdown/src/ts/dropdown.module.ts @@ -0,0 +1,12 @@ +import { RibaModule } from "@ribajs/core"; +import * as components from "./components/index.js"; + +export const DemoModule: RibaModule = { + binders: {}, + components, + formatters: {}, + services: {}, + init() { + return this; + }, +}; diff --git a/demos/tw-dropdown/src/ts/main.ts b/demos/tw-dropdown/src/ts/main.ts new file mode 100644 index 000000000..ac2ba9b00 --- /dev/null +++ b/demos/tw-dropdown/src/ts/main.ts @@ -0,0 +1,14 @@ +import { coreModule, Riba } from "@ribajs/core"; +import { extrasModule } from "@ribajs/extras"; +import { twModule } from "@ribajs/tw"; +import { DemoModule } from "./dropdown.module.js"; + +const riba = new Riba(); +const model = {}; + +riba.module.register(coreModule.init()); +riba.module.register(extrasModule.init()); +riba.module.register(twModule.init()); +riba.module.register(DemoModule.init()); + +riba.bind(document.body, model); diff --git a/demos/tw-dropdown/tsconfig.json b/demos/tw-dropdown/tsconfig.json new file mode 100644 index 000000000..df31a4647 --- /dev/null +++ b/demos/tw-dropdown/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@ribajs/tsconfig/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": [] + }, + "exclude": [ + "node_modules", + "dist/**/*.d.ts" + ] +} diff --git a/demos/tw-dropdown/vite.config.js b/demos/tw-dropdown/vite.config.js new file mode 100644 index 000000000..136a53c10 --- /dev/null +++ b/demos/tw-dropdown/vite.config.js @@ -0,0 +1,6 @@ +import tailwindcss from "@tailwindcss/vite"; +import { ribaViteConfig } from "@ribajs/vite-config"; + +const config = ribaViteConfig(); +config.plugins = [...(config.plugins || []), tailwindcss()]; +export default config; diff --git a/demos/tw-form/package.json b/demos/tw-form/package.json new file mode 100644 index 000000000..5e83beb39 --- /dev/null +++ b/demos/tw-form/package.json @@ -0,0 +1,45 @@ +{ + "name": "@ribajs/demo-tw-form", + "version": "2.0.0-rc.23", + "type": "module", + "engines": { + "node": ">=24.0.0" + }, + "description": "", + "main": "./src/ts/main.ts", + "module": "src/ts/main.ts", + "source": "src/ts/main.ts", + "private": true, + "scripts": { + "build": "vite build --mode production", + "build:dev": "vite build --mode development", + "start": "vite", + "start:dev": "vite", + "serve": "serve dist", + "clear": "rm -rf ./dist", + "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx,.cts,.mts --fix && tsc --noEmit", + "check": "tsc --noEmit" + }, + "files": [ + "src" + ], + "author": "", + "license": "MIT", + "devDependencies": { + "@ribajs/eslint-config": "workspace:^", + "@ribajs/tsconfig": "workspace:^", + "@ribajs/vite-config": "workspace:^", + "@tailwindcss/vite": "^4.2.2", + "eslint": "^10.2.0", + "serve": "^14.2.6", + "typescript": "6.0.2", + "vite": "^8.0.8" + }, + "dependencies": { + "@ribajs/core": "workspace:^", + "@ribajs/extras": "workspace:^", + "@ribajs/tw": "workspace:^", + "@ribajs/utils": "workspace:^", + "tailwindcss": "^4.2.2" + } +} diff --git a/demos/tw-form/src/css/main.css b/demos/tw-form/src/css/main.css new file mode 100644 index 000000000..eec2b775c --- /dev/null +++ b/demos/tw-form/src/css/main.css @@ -0,0 +1,4 @@ +@import "tailwindcss"; +@import "@ribajs/tw/src/css/utilities.css"; + +@custom-variant dark (&:where(.dark, .dark *)); diff --git a/demos/tw-form/src/index.html b/demos/tw-form/src/index.html new file mode 100644 index 000000000..3fb1747af --- /dev/null +++ b/demos/tw-form/src/index.html @@ -0,0 +1,15 @@ + + + + + + TW Form Demo + + + +
    + +
    + + + diff --git a/demos/tw-form/src/ts/components/demo-form/demo-form.component.html b/demos/tw-form/src/ts/components/demo-form/demo-form.component.html new file mode 100644 index 000000000..b8716f0a9 --- /dev/null +++ b/demos/tw-form/src/ts/components/demo-form/demo-form.component.html @@ -0,0 +1,349 @@ +

    TW Form Demo

    + + +
    +

    Registration Form

    +

    A form with various input types and HTML5 validation. Fields marked with * are required.

    +
    + +
    + +
    +
    + + + +
    +
    + + +
    +
    + + +
    + + +

    We will never share your email.

    +
    + + +
    + + +

    Must be at least 8 characters long.

    +
    + + +
    + + +
    + + +
    + + +
    + + +
    +

    Interests

    +
    + + + + +
    +
    + + +
    + +
    + + +
    + + +
    +
    +
    +
    +
    + + +
    +

    Contact Form

    +

    A simpler contact form demonstrating required fields and email validation.

    +
    + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    + +
    + + +
    +
    +
    +
    +
    + + +
    +

    Validation States

    +

    Examples of form fields showing different validation requirements. Try submitting the form to see validation in action.

    +
    + +
    +
    + + +
    +
    + + +
    +
    + + +

    Enter at least 5 characters.

    +
    +
    + + +

    Only alphabetic characters (A-Z, a-z).

    +
    + +
    + + +
    +
    +
    +
    +
    diff --git a/demos/tw-form/src/ts/components/demo-form/demo-form.component.ts b/demos/tw-form/src/ts/components/demo-form/demo-form.component.ts new file mode 100644 index 000000000..a34b0568a --- /dev/null +++ b/demos/tw-form/src/ts/components/demo-form/demo-form.component.ts @@ -0,0 +1,36 @@ +import { Component } from "@ribajs/core"; +import { hasChildNodesTrim } from "@ribajs/utils/src/dom.js"; + +export class DemoFormComponent extends Component { + public static tagName = "demo-form"; + + protected autobind = true; + static get observedAttributes(): string[] { + return []; + } + + public scope = {}; + + constructor() { + super(); + } + + protected connectedCallback() { + super.connectedCallback(); + super.init(DemoFormComponent.observedAttributes); + } + + protected requiredAttributes(): string[] { + return []; + } + + protected async template() { + if (hasChildNodesTrim(this)) { + return null; + } else { + const { default: template } = + await import("./demo-form.component.html?raw"); + return template; + } + } +} diff --git a/demos/tw-form/src/ts/components/index.ts b/demos/tw-form/src/ts/components/index.ts new file mode 100644 index 000000000..10f448d21 --- /dev/null +++ b/demos/tw-form/src/ts/components/index.ts @@ -0,0 +1 @@ +export { DemoFormComponent } from "./demo-form/demo-form.component.js"; diff --git a/demos/tw-form/src/ts/form.module.ts b/demos/tw-form/src/ts/form.module.ts new file mode 100644 index 000000000..064033ae0 --- /dev/null +++ b/demos/tw-form/src/ts/form.module.ts @@ -0,0 +1,12 @@ +import { RibaModule } from "@ribajs/core"; +import * as components from "./components/index.js"; + +export const DemoModule: RibaModule = { + binders: {}, + components, + formatters: {}, + services: {}, + init() { + return this; + }, +}; diff --git a/demos/tw-form/src/ts/main.ts b/demos/tw-form/src/ts/main.ts new file mode 100644 index 000000000..871124d6f --- /dev/null +++ b/demos/tw-form/src/ts/main.ts @@ -0,0 +1,14 @@ +import { coreModule, Riba } from "@ribajs/core"; +import { extrasModule } from "@ribajs/extras"; +import { twModule } from "@ribajs/tw"; +import { DemoModule } from "./form.module.js"; + +const riba = new Riba(); +const model = {}; + +riba.module.register(coreModule.init()); +riba.module.register(extrasModule.init()); +riba.module.register(twModule.init()); +riba.module.register(DemoModule.init()); + +riba.bind(document.body, model); diff --git a/demos/tw-form/tsconfig.json b/demos/tw-form/tsconfig.json new file mode 100644 index 000000000..df31a4647 --- /dev/null +++ b/demos/tw-form/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@ribajs/tsconfig/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": [] + }, + "exclude": [ + "node_modules", + "dist/**/*.d.ts" + ] +} diff --git a/demos/tw-form/vite.config.js b/demos/tw-form/vite.config.js new file mode 100644 index 000000000..136a53c10 --- /dev/null +++ b/demos/tw-form/vite.config.js @@ -0,0 +1,6 @@ +import tailwindcss from "@tailwindcss/vite"; +import { ribaViteConfig } from "@ribajs/vite-config"; + +const config = ribaViteConfig(); +config.plugins = [...(config.plugins || []), tailwindcss()]; +export default config; diff --git a/demos/tw-interactive/package.json b/demos/tw-interactive/package.json new file mode 100644 index 000000000..f43eca7d7 --- /dev/null +++ b/demos/tw-interactive/package.json @@ -0,0 +1,45 @@ +{ + "name": "@ribajs/demo-tw-interactive", + "version": "2.0.0-rc.23", + "type": "module", + "engines": { + "node": ">=24.0.0" + }, + "description": "Interactive components demo for @ribajs/tw", + "main": "./src/ts/main.ts", + "module": "src/ts/main.ts", + "source": "src/ts/main.ts", + "private": true, + "scripts": { + "build": "vite build --mode production", + "build:dev": "vite build --mode development", + "start": "vite", + "start:dev": "vite", + "serve": "serve dist", + "clear": "rm -rf ./dist", + "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx,.cts,.mts --fix && tsc --noEmit", + "check": "tsc --noEmit" + }, + "files": [ + "src" + ], + "author": "", + "license": "MIT", + "devDependencies": { + "@ribajs/eslint-config": "workspace:^", + "@ribajs/tsconfig": "workspace:^", + "@ribajs/vite-config": "workspace:^", + "@tailwindcss/vite": "^4.2.2", + "eslint": "^10.2.0", + "serve": "^14.2.6", + "typescript": "6.0.2", + "vite": "^8.0.8" + }, + "dependencies": { + "@ribajs/core": "workspace:^", + "@ribajs/extras": "workspace:^", + "@ribajs/tw": "workspace:^", + "@ribajs/utils": "workspace:^", + "tailwindcss": "^4.2.2" + } +} diff --git a/demos/tw-interactive/src/css/main.css b/demos/tw-interactive/src/css/main.css new file mode 100644 index 000000000..eec2b775c --- /dev/null +++ b/demos/tw-interactive/src/css/main.css @@ -0,0 +1,4 @@ +@import "tailwindcss"; +@import "@ribajs/tw/src/css/utilities.css"; + +@custom-variant dark (&:where(.dark, .dark *)); diff --git a/demos/tw-interactive/src/index.html b/demos/tw-interactive/src/index.html new file mode 100644 index 000000000..6cafe6f96 --- /dev/null +++ b/demos/tw-interactive/src/index.html @@ -0,0 +1,15 @@ + + + + + + Tailwind Interactive Demo + + + +
    + +
    + + + diff --git a/demos/tw-interactive/src/ts/components/demo-interactive/demo-interactive.component.html b/demos/tw-interactive/src/ts/components/demo-interactive/demo-interactive.component.html new file mode 100644 index 000000000..92c562073 --- /dev/null +++ b/demos/tw-interactive/src/ts/components/demo-interactive/demo-interactive.component.html @@ -0,0 +1,170 @@ +
    + + + + +
    +

    Tailwind Interactive Demo

    + + +
    +

    Tagged Image

    +

    + Click on the numbered pins to reveal details about different areas of the image. +

    +
    + + A historic clock tower rising above the surrounding buildings, dating back to the 19th century. + The bustling main street with shops, cafes, and pedestrians. A hub of daily activity. + Golden hour lighting creating a warm atmospheric glow across the cityscape. + Local vendors selling fresh produce, flowers, and artisanal goods every weekend. + +
    +
    + + +
    +

    Share Button

    +

    + Uses the native Web Share API on supported devices, with a dropdown fallback for desktop browsers. + Try clicking the button to see available sharing options. +

    +
    +
    +

    Share this page

    +

    Spread the word about Riba.js and help other developers discover it.

    + +
    +
    +
    + + +
    +

    Content Slider

    +

    + A multi-column content slider with drag support and navigation controls. Drag to scroll or use the arrow buttons. +

    + +
    +
    +
    +
    + +
    +

    Data Binding

    +

    Declarative two-way data binding with rv-* attributes. Connect your DOM to your model effortlessly.

    +
    +
    +
    +
    +
    + +
    +

    Web Components

    +

    Build custom elements with lifecycle hooks, templates, and automatic scope binding.

    +
    +
    +
    +
    +
    + +
    +

    Formatters

    +

    Pipe syntax for transforming values in templates. Chain multiple formatters with ease.

    +
    +
    +
    +
    +
    + +
    +

    Router

    +

    PJAX-based SPA navigation with smooth transitions, prefetching, and history management.

    +
    +
    +
    +
    +
    + +
    +

    Modules

    +

    Extend Riba with a pluggable module system. Register binders, formatters, and components.

    +
    +
    +
    +
    +
    + +
    +

    TypeScript

    +

    Full TypeScript support with strict mode, generics, and comprehensive type definitions.

    +
    +
    +
    +
    +
    + + +
    +

    Collapse / FAQ

    +

    + Standalone collapsible sections. Each operates independently unlike an accordion. +

    +
    + + +
    + Riba.js is a TypeScript declarative data-binding and Web Components framework. It evolved from Rivets.js and tinybind into a modern, ESM-only library with full TypeScript support. +
    +
    + + +
    + Riba uses rv-* attributes on HTML elements to bind data from your model to the DOM. The Observer pattern with Object.defineProperty ensures changes propagate automatically in both directions. +
    +
    + + +
    + Absolutely! The @ribajs/tw package provides a complete set of Tailwind-styled components including alerts, cards, tabs, sidebars, slideshows, and many more. +
    +
    +
    +
    + + +
    +

    Usage Example

    +

    + Getting started with Riba.js is straightforward. Install the core package and any modules you need, + then bind your model to the DOM. +

    +
    import { Riba, coreModule } from "@ribajs/core";
    +import { twModule } from "@ribajs/tw";
    +
    +const riba = new Riba();
    +riba.module.register(coreModule.init());
    +riba.module.register(twModule.init());
    +riba.bind(document.body, { greeting: "Hello!" });
    +
    +
    +
    diff --git a/demos/tw-interactive/src/ts/components/demo-interactive/demo-interactive.component.ts b/demos/tw-interactive/src/ts/components/demo-interactive/demo-interactive.component.ts new file mode 100644 index 000000000..0493b8311 --- /dev/null +++ b/demos/tw-interactive/src/ts/components/demo-interactive/demo-interactive.component.ts @@ -0,0 +1,36 @@ +import { Component } from "@ribajs/core"; +import { hasChildNodesTrim } from "@ribajs/utils/src/dom.js"; + +export class DemoInteractiveComponent extends Component { + public static tagName = "demo-interactive"; + + protected autobind = true; + static get observedAttributes(): string[] { + return []; + } + + public scope = {}; + + constructor() { + super(); + } + + protected connectedCallback() { + super.connectedCallback(); + super.init(DemoInteractiveComponent.observedAttributes); + } + + protected requiredAttributes(): string[] { + return []; + } + + protected async template() { + if (hasChildNodesTrim(this)) { + return null; + } else { + const { default: template } = + await import("./demo-interactive.component.html?raw"); + return template; + } + } +} diff --git a/demos/tw-interactive/src/ts/components/index.ts b/demos/tw-interactive/src/ts/components/index.ts new file mode 100644 index 000000000..b98ff0c6c --- /dev/null +++ b/demos/tw-interactive/src/ts/components/index.ts @@ -0,0 +1 @@ +export { DemoInteractiveComponent } from "./demo-interactive/demo-interactive.component.js"; diff --git a/demos/tw-interactive/src/ts/interactive.module.ts b/demos/tw-interactive/src/ts/interactive.module.ts new file mode 100644 index 000000000..064033ae0 --- /dev/null +++ b/demos/tw-interactive/src/ts/interactive.module.ts @@ -0,0 +1,12 @@ +import { RibaModule } from "@ribajs/core"; +import * as components from "./components/index.js"; + +export const DemoModule: RibaModule = { + binders: {}, + components, + formatters: {}, + services: {}, + init() { + return this; + }, +}; diff --git a/demos/tw-interactive/src/ts/main.ts b/demos/tw-interactive/src/ts/main.ts new file mode 100644 index 000000000..f3ba299fe --- /dev/null +++ b/demos/tw-interactive/src/ts/main.ts @@ -0,0 +1,14 @@ +import { coreModule, Riba } from "@ribajs/core"; +import { extrasModule } from "@ribajs/extras"; +import { twModule } from "@ribajs/tw"; +import { DemoModule } from "./interactive.module.js"; + +const riba = new Riba(); +const model = {}; + +riba.module.register(coreModule.init()); +riba.module.register(extrasModule.init()); +riba.module.register(twModule.init()); +riba.module.register(DemoModule.init()); + +riba.bind(document.body, model); diff --git a/demos/tw-interactive/tsconfig.json b/demos/tw-interactive/tsconfig.json new file mode 100644 index 000000000..df31a4647 --- /dev/null +++ b/demos/tw-interactive/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@ribajs/tsconfig/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": [] + }, + "exclude": [ + "node_modules", + "dist/**/*.d.ts" + ] +} diff --git a/demos/tw-interactive/vite.config.js b/demos/tw-interactive/vite.config.js new file mode 100644 index 000000000..136a53c10 --- /dev/null +++ b/demos/tw-interactive/vite.config.js @@ -0,0 +1,6 @@ +import tailwindcss from "@tailwindcss/vite"; +import { ribaViteConfig } from "@ribajs/vite-config"; + +const config = ribaViteConfig(); +config.plugins = [...(config.plugins || []), tailwindcss()]; +export default config; diff --git a/demos/tw-notifications/package.json b/demos/tw-notifications/package.json new file mode 100644 index 000000000..4f9d91901 --- /dev/null +++ b/demos/tw-notifications/package.json @@ -0,0 +1,46 @@ +{ + "name": "@ribajs/demo-tw-notifications", + "version": "2.0.0-rc.23", + "type": "module", + "engines": { + "node": ">=24.0.0" + }, + "description": "", + "main": "./src/ts/main.ts", + "module": "src/ts/main.ts", + "source": "src/ts/main.ts", + "private": true, + "scripts": { + "build": "vite build --mode production", + "build:dev": "vite build --mode development", + "start": "vite", + "start:dev": "vite", + "serve": "serve dist", + "clear": "rm -rf ./dist", + "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx,.cts,.mts --fix && tsc --noEmit", + "check": "tsc --noEmit" + }, + "files": [ + "src" + ], + "author": "", + "license": "MIT", + "devDependencies": { + "@ribajs/eslint-config": "workspace:^", + "@ribajs/tsconfig": "workspace:^", + "@ribajs/vite-config": "workspace:^", + "@tailwindcss/vite": "^4.2.2", + "eslint": "^10.2.0", + "serve": "^14.2.6", + "typescript": "6.0.2", + "vite": "^8.0.8" + }, + "dependencies": { + "@ribajs/core": "workspace:^", + "@ribajs/events": "workspace:^", + "@ribajs/extras": "workspace:^", + "@ribajs/tw": "workspace:^", + "@ribajs/utils": "workspace:^", + "tailwindcss": "^4.2.2" + } +} diff --git a/demos/tw-notifications/src/css/main.css b/demos/tw-notifications/src/css/main.css new file mode 100644 index 000000000..eec2b775c --- /dev/null +++ b/demos/tw-notifications/src/css/main.css @@ -0,0 +1,4 @@ +@import "tailwindcss"; +@import "@ribajs/tw/src/css/utilities.css"; + +@custom-variant dark (&:where(.dark, .dark *)); diff --git a/demos/tw-notifications/src/index.html b/demos/tw-notifications/src/index.html new file mode 100644 index 000000000..b46bc84e4 --- /dev/null +++ b/demos/tw-notifications/src/index.html @@ -0,0 +1,15 @@ + + + + + + TW Notifications Demo + + + +
    + +
    + + + diff --git a/demos/tw-notifications/src/ts/components/demo-notifications/demo-notifications.component.html b/demos/tw-notifications/src/ts/components/demo-notifications/demo-notifications.component.html new file mode 100644 index 000000000..1c6175bc4 --- /dev/null +++ b/demos/tw-notifications/src/ts/components/demo-notifications/demo-notifications.component.html @@ -0,0 +1,66 @@ +

    TW Notifications Demo

    + + + + + +
    +

    Toast Notifications

    +

    + Click the buttons below to trigger toast notifications. They appear in the bottom-right corner and auto-dismiss after a timeout. +

    +
    + + + + +
    +
    + + +
    +

    Modal Notification

    +

    + Click the button below to open a modal dialog notification. +

    + +
    + + +
    +

    Inline Alerts

    +

    + Static alert components embedded directly in the page. +

    +
    + + + + +
    +
    diff --git a/demos/tw-notifications/src/ts/components/demo-notifications/demo-notifications.component.ts b/demos/tw-notifications/src/ts/components/demo-notifications/demo-notifications.component.ts new file mode 100644 index 000000000..8221482ff --- /dev/null +++ b/demos/tw-notifications/src/ts/components/demo-notifications/demo-notifications.component.ts @@ -0,0 +1,95 @@ +import { Component } from "@ribajs/core"; +import { EventDispatcher } from "@ribajs/events"; +import { hasChildNodesTrim } from "@ribajs/utils/src/dom.js"; + +export class DemoNotificationsComponent extends Component { + public static tagName = "demo-notifications"; + + protected autobind = true; + static get observedAttributes(): string[] { + return []; + } + + protected toastChannel = new EventDispatcher("toast"); + + public scope = { + showInfoToast: this.showInfoToast.bind(this), + showSuccessToast: this.showSuccessToast.bind(this), + showWarningToast: this.showWarningToast.bind(this), + showErrorToast: this.showErrorToast.bind(this), + showModal: this.showModal.bind(this), + }; + + constructor() { + super(); + } + + protected connectedCallback() { + super.connectedCallback(); + super.init(DemoNotificationsComponent.observedAttributes); + } + + protected requiredAttributes(): string[] { + return []; + } + + public showInfoToast() { + this.toastChannel.trigger("show-notification", { + kind: "toast", + type: "info", + title: "Info", + message: "This is an informational toast notification.", + timeout: 5000, + }); + } + + public showSuccessToast() { + this.toastChannel.trigger("show-notification", { + kind: "toast", + type: "success", + title: "Success", + message: "The operation completed successfully!", + timeout: 5000, + }); + } + + public showWarningToast() { + this.toastChannel.trigger("show-notification", { + kind: "toast", + type: "warning", + title: "Warning", + message: "Please review your input before proceeding.", + timeout: 7000, + }); + } + + public showErrorToast() { + this.toastChannel.trigger("show-notification", { + kind: "toast", + type: "error", + title: "Error", + message: "Something went wrong. Please try again later.", + timeout: 0, + }); + } + + public showModal() { + this.toastChannel.trigger("show-notification", { + kind: "modal", + title: "Confirm Action", + message: + "Are you sure you want to proceed? This action cannot be undone.", + closable: true, + }); + } + + protected async template() { + if (hasChildNodesTrim(this)) { + return null; + } else { + const { default: template } = + await import("./demo-notifications.component.html?raw"); + return template; + } + } +} diff --git a/demos/tw-notifications/src/ts/components/index.ts b/demos/tw-notifications/src/ts/components/index.ts new file mode 100644 index 000000000..2a85c0268 --- /dev/null +++ b/demos/tw-notifications/src/ts/components/index.ts @@ -0,0 +1 @@ +export { DemoNotificationsComponent } from "./demo-notifications/demo-notifications.component.js"; diff --git a/demos/tw-notifications/src/ts/main.ts b/demos/tw-notifications/src/ts/main.ts new file mode 100644 index 000000000..40f30594b --- /dev/null +++ b/demos/tw-notifications/src/ts/main.ts @@ -0,0 +1,14 @@ +import { coreModule, Riba } from "@ribajs/core"; +import { extrasModule } from "@ribajs/extras"; +import { twModule } from "@ribajs/tw"; +import { DemoModule } from "./notifications.module.js"; + +const riba = new Riba(); +const model = {}; + +riba.module.register(coreModule.init()); +riba.module.register(extrasModule.init()); +riba.module.register(twModule.init()); +riba.module.register(DemoModule.init()); + +riba.bind(document.body, model); diff --git a/demos/tw-notifications/src/ts/notifications.module.ts b/demos/tw-notifications/src/ts/notifications.module.ts new file mode 100644 index 000000000..064033ae0 --- /dev/null +++ b/demos/tw-notifications/src/ts/notifications.module.ts @@ -0,0 +1,12 @@ +import { RibaModule } from "@ribajs/core"; +import * as components from "./components/index.js"; + +export const DemoModule: RibaModule = { + binders: {}, + components, + formatters: {}, + services: {}, + init() { + return this; + }, +}; diff --git a/demos/tw-notifications/tsconfig.json b/demos/tw-notifications/tsconfig.json new file mode 100644 index 000000000..df31a4647 --- /dev/null +++ b/demos/tw-notifications/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@ribajs/tsconfig/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": [] + }, + "exclude": [ + "node_modules", + "dist/**/*.d.ts" + ] +} diff --git a/demos/tw-notifications/vite.config.js b/demos/tw-notifications/vite.config.js new file mode 100644 index 000000000..136a53c10 --- /dev/null +++ b/demos/tw-notifications/vite.config.js @@ -0,0 +1,6 @@ +import tailwindcss from "@tailwindcss/vite"; +import { ribaViteConfig } from "@ribajs/vite-config"; + +const config = ribaViteConfig(); +config.plugins = [...(config.plugins || []), tailwindcss()]; +export default config; diff --git a/demos/tw-sidebar/package.json b/demos/tw-sidebar/package.json new file mode 100644 index 000000000..f86d13768 --- /dev/null +++ b/demos/tw-sidebar/package.json @@ -0,0 +1,45 @@ +{ + "name": "@ribajs/demo-tw-sidebar", + "version": "2.0.0-rc.23", + "type": "module", + "engines": { + "node": ">=24.0.0" + }, + "description": "", + "main": "./src/ts/main.ts", + "module": "src/ts/main.ts", + "source": "src/ts/main.ts", + "private": true, + "scripts": { + "build": "vite build --mode production", + "build:dev": "vite build --mode development", + "start": "vite", + "start:dev": "vite", + "serve": "serve dist", + "clear": "rm -rf ./dist", + "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx,.cts,.mts --fix && tsc --noEmit", + "check": "tsc --noEmit" + }, + "files": [ + "src" + ], + "author": "", + "license": "MIT", + "devDependencies": { + "@ribajs/eslint-config": "workspace:^", + "@ribajs/tsconfig": "workspace:^", + "@ribajs/vite-config": "workspace:^", + "@tailwindcss/vite": "^4.2.2", + "eslint": "^10.2.0", + "serve": "^14.2.6", + "typescript": "6.0.2", + "vite": "^8.0.8" + }, + "dependencies": { + "@ribajs/core": "workspace:^", + "@ribajs/extras": "workspace:^", + "@ribajs/tw": "workspace:^", + "@ribajs/utils": "workspace:^", + "tailwindcss": "^4.2.2" + } +} diff --git a/demos/tw-sidebar/src/css/main.css b/demos/tw-sidebar/src/css/main.css new file mode 100644 index 000000000..eec2b775c --- /dev/null +++ b/demos/tw-sidebar/src/css/main.css @@ -0,0 +1,4 @@ +@import "tailwindcss"; +@import "@ribajs/tw/src/css/utilities.css"; + +@custom-variant dark (&:where(.dark, .dark *)); diff --git a/demos/tw-sidebar/src/index.html b/demos/tw-sidebar/src/index.html new file mode 100644 index 000000000..a742c9c84 --- /dev/null +++ b/demos/tw-sidebar/src/index.html @@ -0,0 +1,13 @@ + + + + + + TW Sidebar Demo + + + + + + + diff --git a/demos/tw-sidebar/src/ts/components/demo-sidebar/demo-sidebar.component.html b/demos/tw-sidebar/src/ts/components/demo-sidebar/demo-sidebar.component.html new file mode 100644 index 000000000..0608b6219 --- /dev/null +++ b/demos/tw-sidebar/src/ts/components/demo-sidebar/demo-sidebar.component.html @@ -0,0 +1,158 @@ + + +

    Navigation

    + +
    + + + +

    Details Panel

    +
    +
    +

    Statistics

    +
    +
    + Total Users + 1,234 +
    +
    + Active Today + 567 +
    +
    + New Signups + 89 +
    +
    +
    +
    +
    + + + +

    Side Navigation

    +

    This sidebar pushes the main content aside instead of overlapping it.

    + +
    + + +
    +
    +

    TW Sidebar Demo

    + + +
    +

    Overlap Mode

    +

    + The sidebar floats over the content with a dark backdrop. Click the backdrop or the close button to dismiss. +

    +
    + + + + + + + +
    +
    + + +
    +

    Side Mode

    +

    + The sidebar pushes the main content aside. No backdrop is shown and the content reflows. +

    +
    + + + +
    +
    + + +
    +

    Main Content

    +
    + +

    Welcome to the dashboard. Use the sidebar to navigate between different sections.

    +
    + +

    View and manage your active projects. Click on a project to see its details in the right sidebar.

    +
    + +

    Track your performance metrics. Open the details panel for more information.

    +
    +
    +
    +
    +
    diff --git a/demos/tw-sidebar/src/ts/components/demo-sidebar/demo-sidebar.component.ts b/demos/tw-sidebar/src/ts/components/demo-sidebar/demo-sidebar.component.ts new file mode 100644 index 000000000..eef051808 --- /dev/null +++ b/demos/tw-sidebar/src/ts/components/demo-sidebar/demo-sidebar.component.ts @@ -0,0 +1,36 @@ +import { Component } from "@ribajs/core"; +import { hasChildNodesTrim } from "@ribajs/utils/src/dom.js"; + +export class DemoSidebarComponent extends Component { + public static tagName = "demo-sidebar"; + + protected autobind = true; + static get observedAttributes(): string[] { + return []; + } + + public scope = {}; + + constructor() { + super(); + } + + protected connectedCallback() { + super.connectedCallback(); + super.init(DemoSidebarComponent.observedAttributes); + } + + protected requiredAttributes(): string[] { + return []; + } + + protected async template() { + if (hasChildNodesTrim(this)) { + return null; + } else { + const { default: template } = + await import("./demo-sidebar.component.html?raw"); + return template; + } + } +} diff --git a/demos/tw-sidebar/src/ts/components/index.ts b/demos/tw-sidebar/src/ts/components/index.ts new file mode 100644 index 000000000..93d1d080a --- /dev/null +++ b/demos/tw-sidebar/src/ts/components/index.ts @@ -0,0 +1 @@ +export { DemoSidebarComponent } from "./demo-sidebar/demo-sidebar.component.js"; diff --git a/demos/tw-sidebar/src/ts/main.ts b/demos/tw-sidebar/src/ts/main.ts new file mode 100644 index 000000000..4002dd9f0 --- /dev/null +++ b/demos/tw-sidebar/src/ts/main.ts @@ -0,0 +1,14 @@ +import { coreModule, Riba } from "@ribajs/core"; +import { extrasModule } from "@ribajs/extras"; +import { twModule } from "@ribajs/tw"; +import { DemoModule } from "./sidebar.module.js"; + +const riba = new Riba(); +const model = {}; + +riba.module.register(coreModule.init()); +riba.module.register(extrasModule.init()); +riba.module.register(twModule.init()); +riba.module.register(DemoModule.init()); + +riba.bind(document.body, model); diff --git a/demos/tw-sidebar/src/ts/sidebar.module.ts b/demos/tw-sidebar/src/ts/sidebar.module.ts new file mode 100644 index 000000000..064033ae0 --- /dev/null +++ b/demos/tw-sidebar/src/ts/sidebar.module.ts @@ -0,0 +1,12 @@ +import { RibaModule } from "@ribajs/core"; +import * as components from "./components/index.js"; + +export const DemoModule: RibaModule = { + binders: {}, + components, + formatters: {}, + services: {}, + init() { + return this; + }, +}; diff --git a/demos/tw-sidebar/tsconfig.json b/demos/tw-sidebar/tsconfig.json new file mode 100644 index 000000000..df31a4647 --- /dev/null +++ b/demos/tw-sidebar/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@ribajs/tsconfig/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": [] + }, + "exclude": [ + "node_modules", + "dist/**/*.d.ts" + ] +} diff --git a/demos/tw-sidebar/vite.config.js b/demos/tw-sidebar/vite.config.js new file mode 100644 index 000000000..136a53c10 --- /dev/null +++ b/demos/tw-sidebar/vite.config.js @@ -0,0 +1,6 @@ +import tailwindcss from "@tailwindcss/vite"; +import { ribaViteConfig } from "@ribajs/vite-config"; + +const config = ribaViteConfig(); +config.plugins = [...(config.plugins || []), tailwindcss()]; +export default config; diff --git a/demos/tw-slideshow/package.json b/demos/tw-slideshow/package.json new file mode 100644 index 000000000..e9d54a130 --- /dev/null +++ b/demos/tw-slideshow/package.json @@ -0,0 +1,27 @@ +{ + "name": "@ribajs/demo-tw-slideshow", + "version": "2.0.0-rc.23", + "type": "module", + "main": "./src/ts/main.ts", + "private": true, + "scripts": { + "build": "vite build --mode production", + "start": "vite", + "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx,.cts,.mts --fix && tsc --noEmit", + "check": "tsc --noEmit" + }, + "dependencies": { + "@ribajs/core": "workspace:^", + "@ribajs/extras": "workspace:^", + "@ribajs/tw": "workspace:^", + "@ribajs/utils": "workspace:^", + "@tailwindcss/vite": "^4.2.2", + "tailwindcss": "^4.2.2" + }, + "devDependencies": { + "@ribajs/tsconfig": "workspace:^", + "@ribajs/vite-config": "workspace:^", + "typescript": "6.0.2", + "vite": "^8.0.8" + } +} diff --git a/demos/tw-slideshow/src/css/main.css b/demos/tw-slideshow/src/css/main.css new file mode 100644 index 000000000..eec2b775c --- /dev/null +++ b/demos/tw-slideshow/src/css/main.css @@ -0,0 +1,4 @@ +@import "tailwindcss"; +@import "@ribajs/tw/src/css/utilities.css"; + +@custom-variant dark (&:where(.dark, .dark *)); diff --git a/demos/tw-slideshow/src/index.html b/demos/tw-slideshow/src/index.html new file mode 100644 index 000000000..4833e2a1a --- /dev/null +++ b/demos/tw-slideshow/src/index.html @@ -0,0 +1,15 @@ + + + + + Tailwind Slideshow Demo + + + + + + + + + + diff --git a/demos/tw-slideshow/src/ts/components/demo-slideshow/demo-slideshow.component.html b/demos/tw-slideshow/src/ts/components/demo-slideshow/demo-slideshow.component.html new file mode 100644 index 000000000..f1a8c65fd --- /dev/null +++ b/demos/tw-slideshow/src/ts/components/demo-slideshow/demo-slideshow.component.html @@ -0,0 +1,116 @@ +
    +

    Tailwind Slideshow Demo

    + + +
    +

    Basic Slideshow

    +

    A slideshow with 4 images, prev/next controls, indicator dots, and desktop drag-scroll enabled.

    + +
    +
    + Slide 1 +
    +

    First Slide

    +

    A basic slideshow with controls and indicators

    +
    +
    +
    + Slide 2 +
    +

    Second Slide

    +

    Navigate with arrows or indicators

    +
    +
    +
    + Slide 3 +
    +
    + Slide 4 +
    +
    +
    +
    + + +
    +

    Carousel with Autoplay

    +

    A multi-column carousel that auto-advances every 3 seconds.

    + +
    +
    + Carousel 1 +
    +
    + Carousel 2 +
    +
    + Carousel 3 +
    +
    + Carousel 4 +
    +
    + Carousel 5 +
    +
    + Carousel 6 +
    +
    +
    +
    + + +
    +

    Slideshow with Video Slide

    +

    A slideshow containing a video that auto-plays when its slide is active.

    + +
    +
    + Before video +
    +
    + +
    +
    + After video +
    +
    +
    +
    + + +
    +

    Content Slider

    +

    A multi-column slider for cards or content blocks. Supports drag-scroll and keyboard navigation.

    + +
    +
    + Slider 1 +
    +
    + Slider 2 +
    +
    + Slider 3 +
    +
    + Slider 4 +
    +
    + Slider 5 +
    +
    + Slider 6 +
    +
    +
    +
    +
    diff --git a/demos/tw-slideshow/src/ts/components/demo-slideshow/demo-slideshow.component.ts b/demos/tw-slideshow/src/ts/components/demo-slideshow/demo-slideshow.component.ts new file mode 100644 index 000000000..cebe47279 --- /dev/null +++ b/demos/tw-slideshow/src/ts/components/demo-slideshow/demo-slideshow.component.ts @@ -0,0 +1,37 @@ +import { Component } from "@ribajs/core"; +import { hasChildNodesTrim } from "@ribajs/utils/src/dom.js"; + +export class DemoSlideshowComponent extends Component { + public static tagName = "rv-demo-slideshow"; + + protected autobind = true; + + static get observedAttributes(): string[] { + return []; + } + + public scope = {}; + + constructor() { + super(); + } + + protected connectedCallback() { + super.connectedCallback(); + this.init(DemoSlideshowComponent.observedAttributes); + } + + protected requiredAttributes(): string[] { + return []; + } + + protected async template() { + if (hasChildNodesTrim(this)) { + return null; + } else { + const { default: template } = + await import("./demo-slideshow.component.html?raw"); + return template; + } + } +} diff --git a/demos/tw-slideshow/src/ts/components/index.ts b/demos/tw-slideshow/src/ts/components/index.ts new file mode 100644 index 000000000..1c333530a --- /dev/null +++ b/demos/tw-slideshow/src/ts/components/index.ts @@ -0,0 +1 @@ +export { DemoSlideshowComponent } from "./demo-slideshow/demo-slideshow.component.js"; diff --git a/demos/tw-slideshow/src/ts/main.ts b/demos/tw-slideshow/src/ts/main.ts new file mode 100644 index 000000000..cba784a97 --- /dev/null +++ b/demos/tw-slideshow/src/ts/main.ts @@ -0,0 +1,14 @@ +import { coreModule, Riba } from "@ribajs/core"; +import { extrasModule } from "@ribajs/extras"; +import { twModule } from "@ribajs/tw"; +import { SlideshowDemoModule } from "./slideshow.module.js"; + +const riba = new Riba(); +const model = {}; + +riba.module.register(coreModule.init()); +riba.module.register(extrasModule.init()); +riba.module.register(twModule.init()); +riba.module.register(SlideshowDemoModule.init()); + +riba.bind(document.body, model); diff --git a/demos/tw-slideshow/src/ts/slideshow.module.ts b/demos/tw-slideshow/src/ts/slideshow.module.ts new file mode 100644 index 000000000..08aec5d2d --- /dev/null +++ b/demos/tw-slideshow/src/ts/slideshow.module.ts @@ -0,0 +1,12 @@ +import { RibaModule } from "@ribajs/core"; +import * as components from "./components/index.js"; + +export const SlideshowDemoModule: RibaModule = { + binders: {}, + components, + formatters: {}, + services: {}, + init() { + return this; + }, +}; diff --git a/demos/tw-slideshow/tsconfig.json b/demos/tw-slideshow/tsconfig.json new file mode 100644 index 000000000..7e47a757d --- /dev/null +++ b/demos/tw-slideshow/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@ribajs/tsconfig/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "exclude": ["node_modules", "dist/**/*.d.ts"] +} diff --git a/demos/tw-slideshow/vite.config.js b/demos/tw-slideshow/vite.config.js new file mode 100644 index 000000000..136a53c10 --- /dev/null +++ b/demos/tw-slideshow/vite.config.js @@ -0,0 +1,6 @@ +import tailwindcss from "@tailwindcss/vite"; +import { ribaViteConfig } from "@ribajs/vite-config"; + +const config = ribaViteConfig(); +config.plugins = [...(config.plugins || []), tailwindcss()]; +export default config; diff --git a/demos/tw-tabs/package.json b/demos/tw-tabs/package.json new file mode 100644 index 000000000..9107f4fc0 --- /dev/null +++ b/demos/tw-tabs/package.json @@ -0,0 +1,45 @@ +{ + "name": "@ribajs/demo-tw-tabs", + "version": "2.0.0-rc.23", + "type": "module", + "engines": { + "node": ">=24.0.0" + }, + "description": "", + "main": "./src/ts/main.ts", + "module": "src/ts/main.ts", + "source": "src/ts/main.ts", + "private": true, + "scripts": { + "build": "vite build --mode production", + "build:dev": "vite build --mode development", + "start": "vite", + "start:dev": "vite", + "serve": "serve dist", + "clear": "rm -rf ./dist", + "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx,.cts,.mts --fix && tsc --noEmit", + "check": "tsc --noEmit" + }, + "files": [ + "src" + ], + "author": "", + "license": "MIT", + "devDependencies": { + "@ribajs/eslint-config": "workspace:^", + "@ribajs/tsconfig": "workspace:^", + "@ribajs/vite-config": "workspace:^", + "@tailwindcss/vite": "^4.2.2", + "eslint": "^10.2.0", + "serve": "^14.2.6", + "typescript": "6.0.2", + "vite": "^8.0.8" + }, + "dependencies": { + "@ribajs/core": "workspace:^", + "@ribajs/extras": "workspace:^", + "@ribajs/tw": "workspace:^", + "@ribajs/utils": "workspace:^", + "tailwindcss": "^4.2.2" + } +} diff --git a/demos/tw-tabs/src/css/main.css b/demos/tw-tabs/src/css/main.css new file mode 100644 index 000000000..eec2b775c --- /dev/null +++ b/demos/tw-tabs/src/css/main.css @@ -0,0 +1,4 @@ +@import "tailwindcss"; +@import "@ribajs/tw/src/css/utilities.css"; + +@custom-variant dark (&:where(.dark, .dark *)); diff --git a/demos/tw-tabs/src/index.html b/demos/tw-tabs/src/index.html new file mode 100644 index 000000000..1ef6466d4 --- /dev/null +++ b/demos/tw-tabs/src/index.html @@ -0,0 +1,15 @@ + + + + + + TW Tabs Demo + + + +
    + +
    + + + diff --git a/demos/tw-tabs/src/ts/components/demo-tabs/demo-tabs.component.html b/demos/tw-tabs/src/ts/components/demo-tabs/demo-tabs.component.html new file mode 100644 index 000000000..41ba30ab7 --- /dev/null +++ b/demos/tw-tabs/src/ts/components/demo-tabs/demo-tabs.component.html @@ -0,0 +1,109 @@ +

    TW Tabs + Steps Demo

    + + +
    +

    Tabs

    +

    + Click on the tab headers to switch between content panels. Tabs are defined using <template> children. +

    + + + + + +
    + + +
    +

    Steps Wizard

    +

    + Click on any step circle to navigate to that step. Completed steps show a checkmark. +

    + +

    Horizontal Steps

    + + + + + + +
    + +
    +

    Vertical Steps

    +
    + + + + + + +
    +
    diff --git a/demos/tw-tabs/src/ts/components/demo-tabs/demo-tabs.component.ts b/demos/tw-tabs/src/ts/components/demo-tabs/demo-tabs.component.ts new file mode 100644 index 000000000..6ffbc8730 --- /dev/null +++ b/demos/tw-tabs/src/ts/components/demo-tabs/demo-tabs.component.ts @@ -0,0 +1,36 @@ +import { Component } from "@ribajs/core"; +import { hasChildNodesTrim } from "@ribajs/utils/src/dom.js"; + +export class DemoTabsComponent extends Component { + public static tagName = "demo-tabs"; + + protected autobind = true; + static get observedAttributes(): string[] { + return []; + } + + public scope = {}; + + constructor() { + super(); + } + + protected connectedCallback() { + super.connectedCallback(); + super.init(DemoTabsComponent.observedAttributes); + } + + protected requiredAttributes(): string[] { + return []; + } + + protected async template() { + if (hasChildNodesTrim(this)) { + return null; + } else { + const { default: template } = + await import("./demo-tabs.component.html?raw"); + return template; + } + } +} diff --git a/demos/tw-tabs/src/ts/components/index.ts b/demos/tw-tabs/src/ts/components/index.ts new file mode 100644 index 000000000..d6c345d77 --- /dev/null +++ b/demos/tw-tabs/src/ts/components/index.ts @@ -0,0 +1 @@ +export { DemoTabsComponent } from "./demo-tabs/demo-tabs.component.js"; diff --git a/demos/tw-tabs/src/ts/main.ts b/demos/tw-tabs/src/ts/main.ts new file mode 100644 index 000000000..4575ffd1d --- /dev/null +++ b/demos/tw-tabs/src/ts/main.ts @@ -0,0 +1,14 @@ +import { coreModule, Riba } from "@ribajs/core"; +import { extrasModule } from "@ribajs/extras"; +import { twModule } from "@ribajs/tw"; +import { DemoModule } from "./tabs.module.js"; + +const riba = new Riba(); +const model = {}; + +riba.module.register(coreModule.init()); +riba.module.register(extrasModule.init()); +riba.module.register(twModule.init()); +riba.module.register(DemoModule.init()); + +riba.bind(document.body, model); diff --git a/demos/tw-tabs/src/ts/tabs.module.ts b/demos/tw-tabs/src/ts/tabs.module.ts new file mode 100644 index 000000000..064033ae0 --- /dev/null +++ b/demos/tw-tabs/src/ts/tabs.module.ts @@ -0,0 +1,12 @@ +import { RibaModule } from "@ribajs/core"; +import * as components from "./components/index.js"; + +export const DemoModule: RibaModule = { + binders: {}, + components, + formatters: {}, + services: {}, + init() { + return this; + }, +}; diff --git a/demos/tw-tabs/tsconfig.json b/demos/tw-tabs/tsconfig.json new file mode 100644 index 000000000..df31a4647 --- /dev/null +++ b/demos/tw-tabs/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@ribajs/tsconfig/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": [] + }, + "exclude": [ + "node_modules", + "dist/**/*.d.ts" + ] +} diff --git a/demos/tw-tabs/vite.config.js b/demos/tw-tabs/vite.config.js new file mode 100644 index 000000000..136a53c10 --- /dev/null +++ b/demos/tw-tabs/vite.config.js @@ -0,0 +1,6 @@ +import tailwindcss from "@tailwindcss/vite"; +import { ribaViteConfig } from "@ribajs/vite-config"; + +const config = ribaViteConfig(); +config.plugins = [...(config.plugins || []), tailwindcss()]; +export default config; diff --git a/demos/tw-theme/package.json b/demos/tw-theme/package.json new file mode 100644 index 000000000..35a82829c --- /dev/null +++ b/demos/tw-theme/package.json @@ -0,0 +1,46 @@ +{ + "name": "@ribajs/demo-tw-theme", + "version": "2.0.0-rc.23", + "type": "module", + "engines": { + "node": ">=24.0.0" + }, + "description": "", + "main": "./src/ts/main.ts", + "module": "src/ts/main.ts", + "source": "src/ts/main.ts", + "private": true, + "scripts": { + "build": "vite build --mode production", + "build:dev": "vite build --mode development", + "start": "vite", + "start:dev": "vite", + "serve": "serve dist", + "clear": "rm -rf ./dist", + "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx,.cts,.mts --fix && tsc --noEmit", + "check": "tsc --noEmit" + }, + "files": [ + "src" + ], + "author": "", + "license": "MIT", + "devDependencies": { + "@ribajs/eslint-config": "workspace:^", + "@ribajs/tsconfig": "workspace:^", + "@ribajs/vite-config": "workspace:^", + "@tailwindcss/vite": "^4.2.2", + "eslint": "^10.2.0", + "serve": "^14.2.6", + "typescript": "6.0.2", + "vite": "^8.0.8" + }, + "dependencies": { + "@ribajs/core": "workspace:^", + "@ribajs/events": "workspace:^", + "@ribajs/extras": "workspace:^", + "@ribajs/tw": "workspace:^", + "@ribajs/utils": "workspace:^", + "tailwindcss": "^4.2.2" + } +} diff --git a/demos/tw-theme/src/css/main.css b/demos/tw-theme/src/css/main.css new file mode 100644 index 000000000..eec2b775c --- /dev/null +++ b/demos/tw-theme/src/css/main.css @@ -0,0 +1,4 @@ +@import "tailwindcss"; +@import "@ribajs/tw/src/css/utilities.css"; + +@custom-variant dark (&:where(.dark, .dark *)); diff --git a/demos/tw-theme/src/index.html b/demos/tw-theme/src/index.html new file mode 100644 index 000000000..ab2a9f247 --- /dev/null +++ b/demos/tw-theme/src/index.html @@ -0,0 +1,15 @@ + + + + + + TW Theme Demo + + + +
    + +
    + + + diff --git a/demos/tw-theme/src/ts/components/demo-theme/demo-theme.component.html b/demos/tw-theme/src/ts/components/demo-theme/demo-theme.component.html new file mode 100644 index 000000000..9a436eecf --- /dev/null +++ b/demos/tw-theme/src/ts/components/demo-theme/demo-theme.component.html @@ -0,0 +1,308 @@ + + +

    Navigation

    + +
    + + +

    Details

    +

    + Click the backdrop to dismiss. The backdrop uses + bg-black/50 dark:bg-black/70. +

    +
    + + + + +

    TW Theme Demo

    + + +
    +

    Theme Switcher

    +

    + Use the controls below to switch between light, dark, and system themes. + The entire page will update to reflect the selected theme. +

    + +
    +
    +

    Dropdown Mode

    + +
    + +
    +

    Icon Mode

    + +
    + +
    +

    Large Icon

    + +
    +
    +
    + + +
    +

    Theme-Aware Cards

    +

    + These cards use Tailwind's dark: variant classes to adapt their appearance based on the active theme. +

    + +
    +
    +
    + + + +
    +

    Light Theme

    +

    + A clean, bright appearance with white backgrounds and dark text. Perfect for well-lit environments. +

    +
    + +
    +
    + + + +
    +

    Dark Theme

    +

    + Easy on the eyes with dark backgrounds and light text. Ideal for low-light conditions and reducing eye strain. +

    +
    + +
    +
    + + + +
    +

    System Theme

    +

    + Automatically follows your operating system's theme preference, switching seamlessly between light and dark. +

    +
    +
    +
    + + +
    +

    Typography

    +

    + Text elements that adapt to the current theme using Tailwind dark mode classes. +

    + +
    +

    Heading Text

    +

    + This is body text that adjusts its color based on the active theme. In light mode, it appears as dark gray on a light background. In dark mode, it becomes light gray on a dark background. +

    +

    + This is secondary text with a more subtle appearance, useful for descriptions, captions, and less important information. +

    + + This is a themed link + +
    +
    + + +
    +

    Color Palette

    +

    + See how color swatches adapt between light and dark mode backgrounds. +

    + +
    +
    +
    +

    Primary

    +

    blue-500

    +
    +
    +
    +

    Success

    +

    green-500

    +
    +
    +
    +

    Warning

    +

    yellow-500

    +
    +
    +
    +

    Error

    +

    red-500

    +
    +
    +
    + + +
    +

    Form Elements

    +

    + Form controls that respond to theme changes. +

    + +
    +
    + + +
    +
    + + +
    +
    + + +
    + +
    +
    + + +
    +

    Sidebar (Backdrop Test)

    +

    + Opens a sidebar in overlap mode. The dark backdrop should be noticeably darker in dark mode + (bg-black/70) than in light mode + (bg-black/50). +

    +
    + + + + + + +
    +
    + + +
    +

    Collapse & Accordion

    +

    + Collapse button uses bg-blue-600 dark:bg-blue-700; + the panel below adapts its border and background to the theme. +

    + +
    + + + + + + + +
    +
    + + +
    +

    Dropdown

    +

    + Floating menu surface — tests dark-mode background, hover state and divider color. +

    + + + + +
    + + +
    +

    Alerts

    +

    + All four severity levels in their light and dark variants. +

    +
    + + + + +
    +
    + + +
    +

    Notifications (Second Backdrop Test)

    +

    + The modal renders an additional dark backdrop. Toasts stack in the corner with type-colored surfaces. +

    +
    + + + + + +
    +
    diff --git a/demos/tw-theme/src/ts/components/demo-theme/demo-theme.component.ts b/demos/tw-theme/src/ts/components/demo-theme/demo-theme.component.ts new file mode 100644 index 000000000..97053dd35 --- /dev/null +++ b/demos/tw-theme/src/ts/components/demo-theme/demo-theme.component.ts @@ -0,0 +1,95 @@ +import { Component } from "@ribajs/core"; +import { EventDispatcher } from "@ribajs/events"; +import { hasChildNodesTrim } from "@ribajs/utils/src/dom.js"; + +export class DemoThemeComponent extends Component { + public static tagName = "demo-theme"; + + protected autobind = true; + static get observedAttributes(): string[] { + return []; + } + + protected notifications = new EventDispatcher("theme-demo"); + + public scope = { + showModal: this.showModal.bind(this), + showInfoToast: this.showInfoToast.bind(this), + showSuccessToast: this.showSuccessToast.bind(this), + showWarningToast: this.showWarningToast.bind(this), + showErrorToast: this.showErrorToast.bind(this), + }; + + constructor() { + super(); + } + + protected connectedCallback() { + super.connectedCallback(); + super.init(DemoThemeComponent.observedAttributes); + } + + protected requiredAttributes(): string[] { + return []; + } + + public showModal() { + this.notifications.trigger("show-notification", { + kind: "modal", + title: "Theme Backdrop Test", + message: + "This modal renders a second backdrop. Toggle the theme to compare the light and dark overlay.", + closable: true, + }); + } + + public showInfoToast() { + this.notifications.trigger("show-notification", { + kind: "toast", + type: "info", + title: "Info", + message: "Informational toast in the active theme.", + timeout: 5000, + }); + } + + public showSuccessToast() { + this.notifications.trigger("show-notification", { + kind: "toast", + type: "success", + title: "Success", + message: "Success toast in the active theme.", + timeout: 5000, + }); + } + + public showWarningToast() { + this.notifications.trigger("show-notification", { + kind: "toast", + type: "warning", + title: "Warning", + message: "Warning toast in the active theme.", + timeout: 7000, + }); + } + + public showErrorToast() { + this.notifications.trigger("show-notification", { + kind: "toast", + type: "error", + title: "Error", + message: "Error toast in the active theme.", + timeout: 0, + }); + } + + protected async template() { + if (hasChildNodesTrim(this)) { + return null; + } else { + const { default: template } = + await import("./demo-theme.component.html?raw"); + return template; + } + } +} diff --git a/demos/tw-theme/src/ts/components/index.ts b/demos/tw-theme/src/ts/components/index.ts new file mode 100644 index 000000000..6c85d2a0e --- /dev/null +++ b/demos/tw-theme/src/ts/components/index.ts @@ -0,0 +1 @@ +export { DemoThemeComponent } from "./demo-theme/demo-theme.component.js"; diff --git a/demos/tw-theme/src/ts/main.ts b/demos/tw-theme/src/ts/main.ts new file mode 100644 index 000000000..3ee9c3c21 --- /dev/null +++ b/demos/tw-theme/src/ts/main.ts @@ -0,0 +1,14 @@ +import { coreModule, Riba } from "@ribajs/core"; +import { extrasModule } from "@ribajs/extras"; +import { twModule } from "@ribajs/tw"; +import { DemoModule } from "./theme.module.js"; + +const riba = new Riba(); +const model = {}; + +riba.module.register(coreModule.init()); +riba.module.register(extrasModule.init()); +riba.module.register(twModule.init()); +riba.module.register(DemoModule.init()); + +riba.bind(document.body, model); diff --git a/demos/tw-theme/src/ts/theme.module.ts b/demos/tw-theme/src/ts/theme.module.ts new file mode 100644 index 000000000..064033ae0 --- /dev/null +++ b/demos/tw-theme/src/ts/theme.module.ts @@ -0,0 +1,12 @@ +import { RibaModule } from "@ribajs/core"; +import * as components from "./components/index.js"; + +export const DemoModule: RibaModule = { + binders: {}, + components, + formatters: {}, + services: {}, + init() { + return this; + }, +}; diff --git a/demos/tw-theme/tsconfig.json b/demos/tw-theme/tsconfig.json new file mode 100644 index 000000000..df31a4647 --- /dev/null +++ b/demos/tw-theme/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@ribajs/tsconfig/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": [] + }, + "exclude": [ + "node_modules", + "dist/**/*.d.ts" + ] +} diff --git a/demos/tw-theme/vite.config.js b/demos/tw-theme/vite.config.js new file mode 100644 index 000000000..136a53c10 --- /dev/null +++ b/demos/tw-theme/vite.config.js @@ -0,0 +1,6 @@ +import tailwindcss from "@tailwindcss/vite"; +import { ribaViteConfig } from "@ribajs/vite-config"; + +const config = ribaViteConfig(); +config.plugins = [...(config.plugins || []), tailwindcss()]; +export default config; diff --git a/infra/doc/package.json b/infra/doc/package.json index d57279e48..0001362c8 100644 --- a/infra/doc/package.json +++ b/infra/doc/package.json @@ -43,15 +43,15 @@ "devDependencies": { "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", - "@types/node": "^24.12.0", + "@types/node": "^24.12.2", "@types/prismjs": "^1.26.6", "concurrently": "^9.2.1", "js-yaml": "^4.1.1", - "marked": "^15.0.0", - "pug": "^3.0.3", - "sass": "^1.98.0", + "marked": "^18.0.0", + "pug": "^3.0.4", + "sass": "^1.99.0", "typescript": "^6.0.2", - "vite": "^8.0.3", - "vite-plugin-static-copy": "^3.2.0" + "vite": "^8.0.8", + "vite-plugin-static-copy": "^4.0.1" } } diff --git a/infra/eslint-config/package.json b/infra/eslint-config/package.json index 762a01060..e8a4f1952 100644 --- a/infra/eslint-config/package.json +++ b/infra/eslint-config/package.json @@ -37,14 +37,14 @@ "prettier" ], "dependencies": { - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.32.0", "eslint-plugin-prettier": "^5.5.5", - "prettier": "^3.8.1", + "prettier": "^3.8.2", "typescript": "6.0.2" }, "bugs": { diff --git a/infra/postcss-config/package.json b/infra/postcss-config/package.json index 25fd5867b..c7861970b 100644 --- a/infra/postcss-config/package.json +++ b/infra/postcss-config/package.json @@ -42,8 +42,8 @@ "dependencies": { "@fullhuman/postcss-purgecss": "^8.0.0", "@ribajs/npm-package": "workspace:^", - "postcss": "^8.5.8", - "postcss-preset-env": "^11.2.0" + "postcss": "^8.5.9", + "postcss-preset-env": "^11.2.1" }, "optionalDependencies": { "@ribajs/bs5": "workspace:^", diff --git a/infra/vite-config/package.json b/infra/vite-config/package.json index 466487f10..192c2d563 100644 --- a/infra/vite-config/package.json +++ b/infra/vite-config/package.json @@ -16,6 +16,6 @@ "dependencies": { "@ribajs/iconset": "workspace:^", "rollup-plugin-pug": "^1.1.1", - "vite": "^8.0.3" + "vite": "^8.0.8" } } diff --git a/package.json b/package.json index 23ce530c9..6b9e98b62 100644 --- a/package.json +++ b/package.json @@ -55,24 +55,24 @@ "test:e2e:headed": "playwright test --headed" }, "devDependencies": { - "@playwright/test": "^1.59.0", + "@playwright/test": "^1.59.1", "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@ribajs/types": "workspace:^", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import": "^2.32.0", "eslint-plugin-prettier": "^5.5.5", - "jsdom": "^26.1.0", - "prettier": "^3.8.1", - "sass": "^1.98.0", + "jsdom": "^29.0.2", + "prettier": "^3.8.2", + "sass": "^1.99.0", "typescript": "^6.0.2", - "vite": "^8.0.3", - "vitest": "^3.2.4" + "vite": "^8.0.8", + "vitest": "^4.1.4" }, "workspaces": [ "packages/*", @@ -118,7 +118,8 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/nest-theme": "workspace:^", "@ribajs/npm-package": "workspace:^", - "@ribajs/strapi": "workspace:^" + "@ribajs/strapi": "workspace:^", + "@ribajs/tw": "workspace:^" }, "dependencies": { "@popperjs/core": "^2.11.8", diff --git a/packages/accessibility/package.json b/packages/accessibility/package.json index 86f41b09e..6c36868f5 100644 --- a/packages/accessibility/package.json +++ b/packages/accessibility/package.json @@ -23,8 +23,8 @@ "@ribajs/gamecontroller.js": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@types/eslint": "^9.6.1", - "@types/node": "^24.12.0", - "eslint": "^10.1.0", + "@types/node": "^24.12.2", + "eslint": "^10.2.0", "typescript": "6.0.2" } } diff --git a/packages/artcodestudio/package.json b/packages/artcodestudio/package.json index 27f349fd7..35d4772ff 100644 --- a/packages/artcodestudio/package.json +++ b/packages/artcodestudio/package.json @@ -25,17 +25,17 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/types": "workspace:^", "@types/jest": "^30.0.0", - "@types/node": "^24.12.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", + "@types/node": "^24.12.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "prettier": "^3.8.1", - "ts-jest": "^29.4.6", + "prettier": "^3.8.2", + "ts-jest": "^29.4.9", "typescript": "6.0.2" } } diff --git a/packages/bs5-photoswipe/package.json b/packages/bs5-photoswipe/package.json index 6de7a8cc4..374493a48 100644 --- a/packages/bs5-photoswipe/package.json +++ b/packages/bs5-photoswipe/package.json @@ -39,15 +39,15 @@ "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "prettier": "^3.8.1", - "ts-jest": "^29.4.6", + "prettier": "^3.8.2", + "ts-jest": "^29.4.9", "typescript": "6.0.2" }, "dependencies": { diff --git a/packages/bs5/package.json b/packages/bs5/package.json index a4af6b40d..caf95f39d 100644 --- a/packages/bs5/package.json +++ b/packages/bs5/package.json @@ -54,16 +54,16 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/types": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", "@yarnpkg/pnpify": "^4.1.6", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "prettier": "^3.8.1", - "ts-jest": "^29.4.6", + "prettier": "^3.8.2", + "ts-jest": "^29.4.9", "typescript": "6.0.2" }, "dependencies": { diff --git a/packages/bs5/src/components/bs5-slideshow/bs5-slideshow-slides.component.html b/packages/bs5/src/components/bs5-slideshow/bs5-slideshow-slides.component.html index f838b1119..23a686d75 100644 --- a/packages/bs5/src/components/bs5-slideshow/bs5-slideshow-slides.component.html +++ b/packages/bs5/src/components/bs5-slideshow/bs5-slideshow-slides.component.html @@ -1,3 +1,3 @@
    -
    +
    diff --git a/packages/cache/package.json b/packages/cache/package.json index 3cf87ebc8..9e753deef 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -55,12 +55,12 @@ "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", - "prettier": "^3.8.1", + "prettier": "^3.8.2", "typescript": "6.0.2" }, "dependencies": { diff --git a/packages/content-slider/package.json b/packages/content-slider/package.json index c99d2b5a6..df7a7f6d5 100644 --- a/packages/content-slider/package.json +++ b/packages/content-slider/package.json @@ -39,15 +39,15 @@ "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "prettier": "^3.8.1", - "ts-jest": "^29.4.6", + "prettier": "^3.8.2", + "ts-jest": "^29.4.9", "typescript": "6.0.2" }, "dependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index fc0b7a4b1..f834bdded 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -52,16 +52,16 @@ "@ribajs/tsconfig": "workspace:^", "@ribajs/types": "workspace:^", "@types/jest": "^30.0.0", - "@types/node": "^24.12.0", - "eslint": "^10.1.0", + "@types/node": "^24.12.2", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-config": "^30.3.0", "jest-extended": "^7.0.0", - "prettier": "^3.8.1", + "prettier": "^3.8.2", "source-map-support": "^0.5.21", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2" }, "bugs": { diff --git a/packages/empty-template/package.json b/packages/empty-template/package.json index 53525a626..34b3b93b3 100644 --- a/packages/empty-template/package.json +++ b/packages/empty-template/package.json @@ -23,8 +23,8 @@ "devDependencies": { "@ribajs/tsconfig": "workspace:^", "@types/eslint": "^9.6.1", - "@types/node": "^24.12.0", - "eslint": "^10.1.0", + "@types/node": "^24.12.2", + "eslint": "^10.2.0", "typescript": "6.0.2" } } diff --git a/packages/events/package.json b/packages/events/package.json index e61313b7b..805239799 100644 --- a/packages/events/package.json +++ b/packages/events/package.json @@ -24,12 +24,12 @@ "devDependencies": { "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", - "@types/node": "^24.12.0", - "eslint": "^10.1.0", + "@types/node": "^24.12.2", + "eslint": "^10.2.0", "jest": "^30.3.0", "jest-config": "^30.3.0", "jest-extended": "^7.0.0", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.9", "typescript": "6.0.2" } } diff --git a/packages/extras/package.json b/packages/extras/package.json index f0a6b841a..f3510dbbf 100644 --- a/packages/extras/package.json +++ b/packages/extras/package.json @@ -39,15 +39,15 @@ "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "prettier": "^3.8.1", - "ts-jest": "^29.4.6", + "prettier": "^3.8.2", + "ts-jest": "^29.4.9", "typescript": "6.0.2" }, "dependencies": { diff --git a/packages/extras/src/binders/index.ts b/packages/extras/src/binders/index.ts index 41cfbe084..6348bc21c 100644 --- a/packages/extras/src/binders/index.ts +++ b/packages/extras/src/binders/index.ts @@ -5,3 +5,8 @@ export * from "./scroll-position-angle.binder.js"; export * from "./scrollbar-autoscroll.binder.js"; export * from "./scrollbar-draggable.binder.js"; export * from "./sync-element-property.binder.js"; +export * from "./scroll-to-on-event.binder.js"; +export * from "./toggle-attribute.binder.js"; +export * from "./toggle-class.binder.js"; +export * from "./scrollspy-class.binder.js"; +export * from "./show-toast-on.binder.js"; diff --git a/packages/extras/src/binders/scroll-to-on-event.binder.ts b/packages/extras/src/binders/scroll-to-on-event.binder.ts new file mode 100644 index 000000000..4ddd521d6 --- /dev/null +++ b/packages/extras/src/binders/scroll-to-on-event.binder.ts @@ -0,0 +1,37 @@ +import { Binder } from "@ribajs/core"; +import { scrollTo } from "@ribajs/utils/src/dom.js"; + +export class ScrollToOnEventBinder extends Binder { + static key = "scroll-to-on-*"; + + private target?: HTMLElement; + + private _onEvent(event: Event) { + const offset = Number(this.el.dataset.offset || 0); + const scrollElement = this.el.dataset.scrollElement + ? document.querySelector(this.el.dataset.scrollElement) + : window; + if (this.target) { + scrollTo(this.target, offset, scrollElement); + event.preventDefault(); + } + } + + private onEvent = this._onEvent.bind(this); + + bind(el: HTMLUnknownElement) { + this.onEvent = this.onEvent.bind(this); + const eventName = this.args[0] as string; + el.addEventListener(eventName, this.onEvent); + } + + routine(el: HTMLUnknownElement, targetSelector: string) { + this.target = + document.querySelector(targetSelector) || undefined; + } + + unbind(el: HTMLElement) { + const eventName = this.args[0] as string; + el.removeEventListener(eventName, this.onEvent); + } +} diff --git a/packages/extras/src/binders/scrollspy-class.binder.ts b/packages/extras/src/binders/scrollspy-class.binder.ts new file mode 100644 index 000000000..f91fc6e90 --- /dev/null +++ b/packages/extras/src/binders/scrollspy-class.binder.ts @@ -0,0 +1,69 @@ +import { Binder } from "@ribajs/core"; +import { debounce } from "@ribajs/utils/src/control.js"; +import { isInViewport } from "@ribajs/utils/src/dom.js"; + +/** + * Adds or removes a CSS class based on whether a target element is in the viewport. + * + * Usage: `rv-scrollspy-active="'#section-id'"` + * This will add/remove the class "active" on the bound element when #section-id + * is in the viewport. + */ +export class ScrollspyClassBinder extends Binder { + static key = "scrollspy-*"; + + private target?: HTMLElement; + private className?: string; + + private _onScroll() { + if (!this.target) { + throw new Error("No target element found!"); + } + + if (!this.className) { + throw new Error("className not set!"); + } + + if (this.isInViewport(this.target)) { + this.el.classList.add(this.className); + if (this.el.type === "radio") { + this.el.checked = true; + } + } else { + this.el.classList.remove(this.className); + if (this.el.type === "radio") { + this.el.checked = false; + } + } + } + + private onScroll = debounce(this._onScroll.bind(this)); + + private _isInViewport(elem: Element) { + if (!elem) { + return false; + } + const offsetTop = Number(this.el.dataset.offset || 0); + const offsetBottom = Number(this.el.dataset.offsetBottom || 0); + return isInViewport(elem, { top: offsetTop, bottom: offsetBottom }); + } + + private isInViewport = this._isInViewport.bind(this); + + bind() { + window.addEventListener("scroll", this.onScroll, { + passive: true, + }); + this.onScroll(); + } + + routine(el: HTMLElement, targetSelector: string) { + const nativeIDTargetSelector = targetSelector.replace("#", ""); + this.target = document.getElementById(nativeIDTargetSelector) || undefined; + this.className = this.args[0] as string; + } + + unbind() { + window.removeEventListener("scroll", this.onScroll); + } +} diff --git a/packages/extras/src/binders/show-toast-on.binder.ts b/packages/extras/src/binders/show-toast-on.binder.ts new file mode 100644 index 000000000..4afacc4be --- /dev/null +++ b/packages/extras/src/binders/show-toast-on.binder.ts @@ -0,0 +1,45 @@ +import { Binder } from "@ribajs/core"; +import { EventDispatcher } from "@ribajs/events"; +import type { ToastNotification } from "../types/notification.js"; + +/** + * Shows a toast notification when a specified event fires on the element. + * + * Usage: `rv-show-toast-on-click="toastData"` + */ +export class ShowToastOnEventBinder extends Binder< + ToastNotification, + HTMLElement +> { + static key = "show-toast-on-*"; + + private toastData?: ToastNotification; + + private _onEvent() { + if (!this.toastData) { + throw new Error("Toast data not set!"); + } + const toastData: ToastNotification = { ...this.toastData }; + const notificationDispatcher = new EventDispatcher("toast"); + notificationDispatcher.trigger("show-notification", toastData); + } + + private onEvent = this._onEvent.bind(this); + + bind(el: HTMLElement) { + const eventName = this.args[0] as string; + el.addEventListener(eventName as any, this.onEvent); + } + + routine(_el: HTMLElement, toastData: ToastNotification) { + if (this.args === null) { + throw new Error("args is null"); + } + this.toastData = toastData; + } + + unbind(el: HTMLElement) { + const eventName = this.args[0] as string; + el.removeEventListener(eventName as any, this.onEvent); + } +} diff --git a/packages/extras/src/binders/toggle-attribute.binder.ts b/packages/extras/src/binders/toggle-attribute.binder.ts new file mode 100644 index 000000000..15c14c888 --- /dev/null +++ b/packages/extras/src/binders/toggle-attribute.binder.ts @@ -0,0 +1,97 @@ +import { Binder } from "@ribajs/core"; +import { EventDispatcher } from "@ribajs/events"; +import { TOGGLE_BUTTON, TOGGLE_ATTRIBUTE } from "../constants/index.js"; + +/** + * Framework-agnostic toggle attribute binder. + * Adds / removes an attribute via toggle-button EventDispatcher. + */ +export class ToggleAttributeBinder extends Binder { + static key = "toggle-attribute-*"; + + private toggleButtonEvents?: EventDispatcher; + private state = "off"; + + private _triggerState() { + this.toggleButtonEvents?.trigger( + TOGGLE_BUTTON.eventNames.state, + this.state, + ); + } + + private triggerState = this._triggerState.bind(this); + + private _onToggle() { + this.toggle.bind(this)(this.el); + } + + private onToggle = this._onToggle.bind(this); + + private toggle(el: HTMLElement) { + if (this.state === "removed") { + this.add.bind(this)(el); + } else { + this.remove.bind(this)(el); + } + } + + private remove(el: HTMLElement) { + const attributeName = this.args[0] as string; + el.removeAttribute(attributeName); + this.state = "removed"; + el.dispatchEvent( + new CustomEvent(TOGGLE_ATTRIBUTE.elEventNames.removed, { + detail: { attributeName }, + }), + ); + this.triggerState(); + } + + private add(el: HTMLElement) { + const attributeName = this.args[0] as string; + el.setAttribute(attributeName, attributeName); + this.state = "added"; + el.dispatchEvent( + new CustomEvent(TOGGLE_ATTRIBUTE.elEventNames.added, { + detail: { attributeName }, + }), + ); + this.triggerState(); + } + + bind(el: HTMLElement) { + const attributeName = this.args[0] as string; + this.state = el.hasAttribute(attributeName) ? "added" : "removed"; + } + + unbind() { + this.toggleButtonEvents?.off( + TOGGLE_BUTTON.eventNames.toggle, + this.onToggle, + this, + ); + this.toggleButtonEvents?.off( + TOGGLE_BUTTON.eventNames.init, + this.triggerState, + this, + ); + } + + routine(el: HTMLElement, newId: string) { + const oldId = this._getValue(el); + let toggleButton = this.toggleButtonEvents; + if (oldId && toggleButton) { + toggleButton.off(TOGGLE_BUTTON.eventNames.toggle, this.onToggle, this); + toggleButton.off(TOGGLE_BUTTON.eventNames.init, this.triggerState, this); + } + + if (!this.toggleButtonEvents) { + this.toggleButtonEvents = new EventDispatcher( + TOGGLE_BUTTON.nsPrefix + newId, + ); + toggleButton = this.toggleButtonEvents as EventDispatcher; + toggleButton.on(TOGGLE_BUTTON.eventNames.toggle, this.onToggle, this); + toggleButton.on(TOGGLE_BUTTON.eventNames.init, this.triggerState, this); + } + } +} diff --git a/packages/extras/src/binders/toggle-class.binder.ts b/packages/extras/src/binders/toggle-class.binder.ts new file mode 100644 index 000000000..6768edd73 --- /dev/null +++ b/packages/extras/src/binders/toggle-class.binder.ts @@ -0,0 +1,97 @@ +import { Binder } from "@ribajs/core"; +import { EventDispatcher } from "@ribajs/events"; +import { TOGGLE_BUTTON, TOGGLE_CLASS } from "../constants/index.js"; + +/** + * Framework-agnostic toggle class binder. + * Adds / removes a CSS class via toggle-button EventDispatcher. + */ +export class ToggleClassBinder extends Binder { + static key = "toggle-class-*"; + + private toggleButtonEvents?: EventDispatcher; + private state = "off"; + + private _triggerState() { + this.toggleButtonEvents?.trigger( + TOGGLE_BUTTON.eventNames.state, + this.state, + ); + } + + private triggerState = this._triggerState.bind(this); + + private _onToggle() { + this.toggle.bind(this)(this.el); + } + + private onToggle = this._onToggle.bind(this); + + private toggle(el: HTMLButtonElement) { + if (this.state === "removed") { + this.add.bind(this)(el); + } else { + this.remove.bind(this)(el); + } + } + + private remove(el: HTMLButtonElement) { + const className = this.args[0] as string; + el.classList.remove(className); + this.state = "removed"; + el.dispatchEvent( + new CustomEvent(TOGGLE_CLASS.elEventNames.removed, { + detail: { className }, + }), + ); + this.triggerState(); + } + + private add(el: HTMLButtonElement) { + const className = this.args[0] as string; + el.classList.add(className); + this.state = "added"; + el.dispatchEvent( + new CustomEvent(TOGGLE_CLASS.elEventNames.added, { + detail: { className }, + }), + ); + this.triggerState(); + } + + bind(el: HTMLButtonElement) { + const className = this.args[0] as string; + this.state = el.classList.contains(className) ? "added" : "removed"; + } + + unbind() { + this.toggleButtonEvents?.off( + TOGGLE_BUTTON.eventNames.toggle, + this.onToggle, + this, + ); + this.toggleButtonEvents?.off( + TOGGLE_BUTTON.eventNames.init, + this.triggerState, + this, + ); + } + + routine(el: HTMLButtonElement, newId: string) { + const oldId = this._getValue(el); + let toggleButton = this.toggleButtonEvents; + if (oldId && toggleButton) { + toggleButton.off(TOGGLE_BUTTON.eventNames.toggle, this.onToggle, this); + toggleButton.off(TOGGLE_BUTTON.eventNames.init, this.triggerState, this); + } + + if (!this.toggleButtonEvents) { + this.toggleButtonEvents = new EventDispatcher( + TOGGLE_BUTTON.nsPrefix + newId, + ); + toggleButton = this.toggleButtonEvents as EventDispatcher; + toggleButton.on(TOGGLE_BUTTON.eventNames.toggle, this.onToggle, this); + toggleButton.on(TOGGLE_BUTTON.eventNames.init, this.triggerState, this); + } + } +} diff --git a/packages/extras/src/constants/index.ts b/packages/extras/src/constants/index.ts new file mode 100644 index 000000000..ed8345696 --- /dev/null +++ b/packages/extras/src/constants/index.ts @@ -0,0 +1,23 @@ +export const TOGGLE_BUTTON = { + nsPrefix: "toggle-button-", + eventNames: { + toggle: "toggle", + toggled: "toggled", + init: "init", + state: "state", + }, +}; + +export const TOGGLE_ATTRIBUTE = { + elEventNames: { + removed: "attribute-removed", + added: "attribute-added", + }, +}; + +export const TOGGLE_CLASS = { + elEventNames: { + removed: "class-removed", + added: "class-added", + }, +}; diff --git a/packages/extras/src/index.ts b/packages/extras/src/index.ts index 6f1d749c5..abb841782 100644 --- a/packages/extras/src/index.ts +++ b/packages/extras/src/index.ts @@ -1,4 +1,5 @@ export * from "./binders/index.js"; +export * from "./constants/index.js"; export * from "./components/index.js"; export * from "./helper/index.js"; export * from "./services/index.js"; diff --git a/packages/extras/src/services/dragscroll.service.ts b/packages/extras/src/services/dragscroll.service.ts index a880d7a75..9d315ac4f 100644 --- a/packages/extras/src/services/dragscroll.service.ts +++ b/packages/extras/src/services/dragscroll.service.ts @@ -24,8 +24,14 @@ export class Dragscroll { this.el = el; this.options = options; + console.debug("[Dragscroll] constructor", { + el: el.tagName + "." + el.className.split(" ").slice(0, 3).join("."), + touchCapable: this.touchCapable, + detectGlobalMove: options.detectGlobalMove, + }); + if (this.touchCapable) { - // Do noting on touch devices + console.debug("[Dragscroll] Skipping — touch device detected"); return this; } @@ -84,12 +90,19 @@ export class Dragscroll { public checkDraggable = throttle(this._checkDraggable.bind(this)); protected onMouseDown(e: MouseEvent) { + console.debug("[Dragscroll] mousedown", { + clientX: e.clientX, + clientY: e.clientY, + }); this.pushed = true; this.lastClientX = e.clientX; this.lastClientY = e.clientY; } protected onMouseUp() { + if (this.pushed) { + console.debug("[Dragscroll] mouseup"); + } this.pushed = false; } diff --git a/packages/extras/src/services/index.ts b/packages/extras/src/services/index.ts index 034cb17f7..b3dfa0e42 100644 --- a/packages/extras/src/services/index.ts +++ b/packages/extras/src/services/index.ts @@ -5,3 +5,4 @@ export * from "./fullscreen.service.js"; export * from "./gameloop.service.js"; export * from "./touch-events/scroll-events.service.js"; export * from "./touch-events/touch-events.service.js"; +export * from "./modal.service.js"; diff --git a/packages/extras/src/services/modal.service.spec.ts b/packages/extras/src/services/modal.service.spec.ts new file mode 100644 index 000000000..7ea14878e --- /dev/null +++ b/packages/extras/src/services/modal.service.spec.ts @@ -0,0 +1,202 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { ModalService } from "./modal.service.js"; + +describe("ModalService", () => { + let dialog: HTMLDialogElement; + + beforeEach(() => { + dialog = document.createElement("dialog") as HTMLDialogElement; + // jsdom does not implement showModal()/close() on + dialog.showModal = vi.fn(); + dialog.close = vi.fn(); + document.body.appendChild(dialog); + }); + + afterEach(() => { + dialog.remove(); + document.body.style.overflow = ""; + }); + + describe("constructor", () => { + it("creates service without throwing", () => { + expect(() => new ModalService(dialog)).not.toThrow(); + }); + }); + + describe("isShown", () => { + it("defaults to false", () => { + const modal = new ModalService(dialog); + expect(modal.isShown).toBe(false); + }); + }); + + describe("show()", () => { + it("calls showModal() on the dialog element", () => { + const modal = new ModalService(dialog); + modal.show(); + expect(dialog.showModal).toHaveBeenCalledTimes(1); + }); + + it("sets body overflow to hidden", () => { + const modal = new ModalService(dialog); + modal.show(); + expect(document.body.style.overflow).toBe("hidden"); + }); + + it("sets isShown to true", () => { + const modal = new ModalService(dialog); + modal.show(); + expect(modal.isShown).toBe(true); + }); + + it("dispatches modal.show event", () => { + const modal = new ModalService(dialog); + const handler = vi.fn(); + dialog.addEventListener("modal.show", handler); + modal.show(); + expect(handler).toHaveBeenCalledTimes(1); + }); + + it("dispatches modal.shown event", () => { + const modal = new ModalService(dialog); + const handler = vi.fn(); + dialog.addEventListener("modal.shown", handler); + modal.show(); + expect(handler).toHaveBeenCalledTimes(1); + }); + + it("does nothing when already shown", () => { + const modal = new ModalService(dialog); + modal.show(); + (dialog.showModal as ReturnType).mockClear(); + modal.show(); + expect(dialog.showModal).not.toHaveBeenCalled(); + }); + }); + + describe("hide()", () => { + it("calls close() on the dialog element", () => { + const modal = new ModalService(dialog); + modal.show(); + modal.hide(); + expect(dialog.close).toHaveBeenCalledTimes(1); + }); + + it("restores body overflow", () => { + const modal = new ModalService(dialog); + modal.show(); + expect(document.body.style.overflow).toBe("hidden"); + modal.hide(); + expect(document.body.style.overflow).toBe(""); + }); + + it("sets isShown to false", () => { + const modal = new ModalService(dialog); + modal.show(); + modal.hide(); + expect(modal.isShown).toBe(false); + }); + + it("dispatches modal.hide event", () => { + const modal = new ModalService(dialog); + modal.show(); + const handler = vi.fn(); + dialog.addEventListener("modal.hide", handler); + modal.hide(); + expect(handler).toHaveBeenCalledTimes(1); + }); + + it("dispatches modal.hidden event", () => { + const modal = new ModalService(dialog); + modal.show(); + const handler = vi.fn(); + dialog.addEventListener("modal.hidden", handler); + modal.hide(); + expect(handler).toHaveBeenCalledTimes(1); + }); + + it("does nothing when already hidden", () => { + const modal = new ModalService(dialog); + const handler = vi.fn(); + dialog.addEventListener("modal.hide", handler); + modal.hide(); + expect(handler).not.toHaveBeenCalled(); + }); + }); + + describe("toggle()", () => { + it("shows when currently hidden", () => { + const modal = new ModalService(dialog); + modal.toggle(); + expect(modal.isShown).toBe(true); + }); + + it("hides when currently shown", () => { + const modal = new ModalService(dialog); + modal.show(); + modal.toggle(); + expect(modal.isShown).toBe(false); + }); + + it("round-trips correctly: hidden -> shown -> hidden", () => { + const modal = new ModalService(dialog); + modal.toggle(); + expect(modal.isShown).toBe(true); + modal.toggle(); + expect(modal.isShown).toBe(false); + }); + }); + + describe("Escape key", () => { + it("triggers hide when Escape is pressed", () => { + const modal = new ModalService(dialog); + modal.show(); + const event = new KeyboardEvent("keydown", { + key: "Escape", + bubbles: true, + }); + dialog.dispatchEvent(event); + expect(modal.isShown).toBe(false); + }); + + it("does not hide when a different key is pressed", () => { + const modal = new ModalService(dialog); + modal.show(); + const event = new KeyboardEvent("keydown", { + key: "Enter", + bubbles: true, + }); + dialog.dispatchEvent(event); + expect(modal.isShown).toBe(true); + }); + }); + + describe("dispose()", () => { + it("hides the modal if shown", () => { + const modal = new ModalService(dialog); + modal.show(); + modal.dispose(); + expect(modal.isShown).toBe(false); + expect(dialog.close).toHaveBeenCalled(); + }); + + it("removes event listeners (Escape no longer triggers hide)", () => { + const modal = new ModalService(dialog); + modal.show(); + modal.dispose(); + const handler = vi.fn(); + dialog.addEventListener("modal.hide", handler); + const event = new KeyboardEvent("keydown", { + key: "Escape", + bubbles: true, + }); + dialog.dispatchEvent(event); + expect(handler).not.toHaveBeenCalled(); + }); + + it("does not throw when called on an already-hidden modal", () => { + const modal = new ModalService(dialog); + expect(() => modal.dispose()).not.toThrow(); + }); + }); +}); diff --git a/packages/extras/src/services/modal.service.ts b/packages/extras/src/services/modal.service.ts new file mode 100644 index 000000000..63d41664a --- /dev/null +++ b/packages/extras/src/services/modal.service.ts @@ -0,0 +1,88 @@ +/** + * Framework-agnostic modal service. + * + * Uses the native `` element with scroll lock + optional backdrop-click + * to close. Dispatches `modal.show`, `modal.shown`, `modal.hide`, `modal.hidden` + * events on the element. + */ +export class ModalService { + protected el: HTMLDialogElement; + protected backdrop: HTMLElement | null = null; + protected _isShown = false; + protected abortController = new AbortController(); + + constructor( + el: HTMLDialogElement, + options: { backdrop?: boolean; keyboard?: boolean } = {}, + ) { + this.el = el; + const signal = this.abortController.signal; + + this.el.addEventListener("keydown", this._onKeydown, { signal }); + + if (options.backdrop !== false) { + this.el.addEventListener("click", this._onBackdropClick, { signal }); + } + } + + get isShown() { + return this._isShown; + } + + show() { + if (this._isShown) return; + + this.el.dispatchEvent(new CustomEvent("modal.show")); + + document.body.style.overflow = "hidden"; + + this.el.showModal(); + this._isShown = true; + + this.el.dispatchEvent(new CustomEvent("modal.shown")); + } + + hide() { + if (!this._isShown) return; + + this.el.dispatchEvent(new CustomEvent("modal.hide")); + + this.el.close(); + document.body.style.overflow = ""; + this._isShown = false; + + this.el.dispatchEvent(new CustomEvent("modal.hidden")); + } + + toggle() { + if (this._isShown) { + this.hide(); + } else { + this.show(); + } + } + + private _onKeydown = (event: KeyboardEvent) => { + if (event.key === "Escape" && this._isShown) { + event.preventDefault(); + this.hide(); + } + }; + + /** + * Close when clicking the dialog backdrop (the ::backdrop pseudo-element). + * A click on the dialog itself (not its children) means the backdrop was clicked. + */ + private _onBackdropClick = (event: MouseEvent) => { + if (event.target === this.el) { + this.hide(); + } + }; + + dispose() { + this.abortController.abort(); + if (this._isShown) { + this.hide(); + } + } +} diff --git a/packages/extras/src/types/index.ts b/packages/extras/src/types/index.ts index 01d536b4a..0dfefa4ba 100644 --- a/packages/extras/src/types/index.ts +++ b/packages/extras/src/types/index.ts @@ -16,3 +16,4 @@ export * from "./touch-settings.js"; export * from "./touch-swipe-data.js"; export * from "./touch-type.js"; export * from "./video-component-scope.js"; +export * from "./notification.js"; diff --git a/packages/extras/src/types/notification.ts b/packages/extras/src/types/notification.ts new file mode 100644 index 000000000..c253adf74 --- /dev/null +++ b/packages/extras/src/types/notification.ts @@ -0,0 +1,34 @@ +export interface Notification { + title?: string; + message: string; + /** + * Notification kind: "toast" for toast notifications, "modal" for modal dialogs. + */ + kind: "toast" | "modal"; + /** + * Visual style type for the notification. + */ + type?: "info" | "success" | "warning" | "error"; + /** + * Auto-dismiss timeout in milliseconds. 0 means no auto-dismiss. + */ + timeout?: number; +} + +export interface ModalNotification extends Notification { + kind: "modal"; + /** Show close button */ + closable?: boolean; +} + +export interface ToastNotification extends Notification { + kind: "toast"; + /** Position of the toast */ + position?: + | "top-right" + | "top-left" + | "bottom-right" + | "bottom-left" + | "top-center" + | "bottom-center"; +} diff --git a/packages/extras/tsconfig.json b/packages/extras/tsconfig.json index 87cbfa3d5..f21735372 100644 --- a/packages/extras/tsconfig.json +++ b/packages/extras/tsconfig.json @@ -8,6 +8,7 @@ "include": ["src/**/*"], "exclude": [ "node_modules", - "dist/**/*.d.ts" + "dist/**/*.d.ts", + "src/**/*.spec.ts" ] } diff --git a/packages/fuse/package.json b/packages/fuse/package.json index a464d86df..c7070f367 100644 --- a/packages/fuse/package.json +++ b/packages/fuse/package.json @@ -16,7 +16,7 @@ "@ribajs/core": "workspace:^", "@ribajs/jsx": "workspace:^", "@ribajs/utils": "workspace:^", - "fuse.js": "^7.1.0" + "fuse.js": "^7.3.0" }, "main": "src/index.ts", "module": "src/index.ts", @@ -24,8 +24,8 @@ "devDependencies": { "@ribajs/tsconfig": "workspace:^", "@types/eslint": "^9.6.1", - "@types/node": "^24.12.0", - "eslint": "^10.1.0", + "@types/node": "^24.12.2", + "eslint": "^10.2.0", "typescript": "6.0.2" } } diff --git a/packages/fuse/src/components/search/search.component.tsx b/packages/fuse/src/components/search/search.component.tsx index 1e80e4654..bb3282df1 100644 --- a/packages/fuse/src/components/search/search.component.tsx +++ b/packages/fuse/src/components/search/search.component.tsx @@ -36,7 +36,7 @@ export class FuseSearchComponent extends Component { console.warn("fuse is not ready! Did you forget to call initFuse?"); return []; } - this.scope.results = this.fuse?.search(this.scope.searchPattern) || []; + this.scope.results = this.fuse?.search(this.scope.searchPattern) || []; console.debug("on search", this.scope.searchPattern, this.scope.results); } diff --git a/packages/history/package.json b/packages/history/package.json index b1ae581b4..4a2a09a6a 100644 --- a/packages/history/package.json +++ b/packages/history/package.json @@ -54,7 +54,7 @@ "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "typescript": "6.0.2" } } diff --git a/packages/i18n/package.json b/packages/i18n/package.json index bb169840e..55e20a1cb 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -38,12 +38,12 @@ "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", - "prettier": "^3.8.1", + "prettier": "^3.8.2", "typescript": "6.0.2" }, "dependencies": { diff --git a/packages/jquery/package.json b/packages/jquery/package.json index 051ccc64a..e4b95c7fa 100644 --- a/packages/jquery/package.json +++ b/packages/jquery/package.json @@ -39,16 +39,16 @@ "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-config": "^30.3.0", "jest-extended": "^7.0.0", - "prettier": "^3.8.1", - "ts-jest": "^29.4.6", + "prettier": "^3.8.2", + "ts-jest": "^29.4.9", "typescript": "6.0.2" }, "dependencies": { diff --git a/packages/jsx/package.json b/packages/jsx/package.json index c0605f09d..4b68dc40e 100644 --- a/packages/jsx/package.json +++ b/packages/jsx/package.json @@ -21,8 +21,8 @@ "devDependencies": { "@ribajs/tsconfig": "workspace:^", "@types/eslint": "^9.6.1", - "@types/node": "^24.12.0", - "eslint": "^10.1.0", + "@types/node": "^24.12.2", + "eslint": "^10.2.0", "typescript": "6.0.2" } } diff --git a/packages/jsx/src/jsx.ts b/packages/jsx/src/jsx.ts index ef81a102c..c65ce03e5 100644 --- a/packages/jsx/src/jsx.ts +++ b/packages/jsx/src/jsx.ts @@ -76,6 +76,8 @@ export function renderElement(element: JsxElement | null | undefined): string { for (const [key, val] of Object.entries(props ?? {})) { if (val == null) continue; + // Skip React/esbuild dev-only props that should never be rendered to DOM + if (key === "__self" || key === "__source") continue; if (typeof val == "boolean" && !isCustomElement(tag)) { if (val) { diff --git a/packages/leaflet-map/package.json b/packages/leaflet-map/package.json index 29dbce6f9..5ab9ccc3c 100644 --- a/packages/leaflet-map/package.json +++ b/packages/leaflet-map/package.json @@ -39,15 +39,15 @@ "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "prettier": "^3.8.1", - "ts-jest": "^29.4.6", + "prettier": "^3.8.2", + "ts-jest": "^29.4.9", "typescript": "6.0.2" }, "dependencies": { diff --git a/packages/lottie/package.json b/packages/lottie/package.json index f013ee042..b46c7bb8a 100644 --- a/packages/lottie/package.json +++ b/packages/lottie/package.json @@ -26,9 +26,9 @@ "devDependencies": { "@ribajs/tsconfig": "workspace:^", "@types/eslint": "^9.6.1", - "@types/node": "^24.12.0", - "eslint": "^10.1.0", - "sass": "^1.98.0", + "@types/node": "^24.12.2", + "eslint": "^10.2.0", + "sass": "^1.99.0", "typescript": "6.0.2" } } diff --git a/packages/luxon/package.json b/packages/luxon/package.json index aa25f488e..333795ca7 100644 --- a/packages/luxon/package.json +++ b/packages/luxon/package.json @@ -48,15 +48,15 @@ "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "prettier": "^3.8.1", - "ts-jest": "^29.4.6", + "prettier": "^3.8.2", + "ts-jest": "^29.4.9", "typescript": "6.0.2" }, "dependencies": { diff --git a/packages/masonry/package.json b/packages/masonry/package.json index 154b22cf0..17edde729 100644 --- a/packages/masonry/package.json +++ b/packages/masonry/package.json @@ -25,15 +25,15 @@ "@types/imagesloaded": "^4.1.7", "@types/jest": "^30.0.0", "@types/masonry-layout": "^4.2.8", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "prettier": "^3.8.1", - "ts-jest": "^29.4.6", + "prettier": "^3.8.2", + "ts-jest": "^29.4.9", "typescript": "6.0.2" } } diff --git a/packages/moment/package.json b/packages/moment/package.json index 2d4f6cc3c..b5ca2aa89 100644 --- a/packages/moment/package.json +++ b/packages/moment/package.json @@ -51,16 +51,16 @@ "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", "jest-ts-webcompat-resolver": "^1.0.1", - "prettier": "^3.8.1", - "ts-jest": "^29.4.6", + "prettier": "^3.8.2", + "ts-jest": "^29.4.9", "typescript": "6.0.2" }, "dependencies": { diff --git a/packages/monaco-editor/package.json b/packages/monaco-editor/package.json index c0e4c6e84..06cfc859f 100644 --- a/packages/monaco-editor/package.json +++ b/packages/monaco-editor/package.json @@ -49,15 +49,15 @@ "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "prettier": "^3.8.1", - "ts-jest": "^29.4.6", + "prettier": "^3.8.2", + "ts-jest": "^29.4.9", "typescript": "6.0.2" }, "dependencies": { diff --git a/packages/octobercms/package.json b/packages/octobercms/package.json index 1d390d8cb..0bdad40ab 100644 --- a/packages/octobercms/package.json +++ b/packages/octobercms/package.json @@ -38,16 +38,16 @@ "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", "@types/jquery": "^4.0.0", - "@types/node": "^24.12.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", + "@types/node": "^24.12.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "jest": "^30.3.0", "jest-extended": "^7.0.0", - "prettier": "^3.8.1", - "ts-jest": "^29.4.6", + "prettier": "^3.8.2", + "ts-jest": "^29.4.9", "typescript": "6.0.2" }, "dependencies": { diff --git a/packages/podcast/package.json b/packages/podcast/package.json index 2b183f061..e6097513b 100644 --- a/packages/podcast/package.json +++ b/packages/podcast/package.json @@ -29,9 +29,9 @@ }, "devDependencies": { "@ribajs/tsconfig": "workspace:^", - "@types/node": "^24.12.0", + "@types/node": "^24.12.2", "concurrently": "^9.2.1", - "eslint": "^10.1.0", + "eslint": "^10.2.0", "typescript": "6.0.2" } } diff --git a/packages/router/package.json b/packages/router/package.json index 1812604e2..bbf607f97 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -50,15 +50,15 @@ "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^8.57.2", - "@typescript-eslint/typescript-estree": "^8.57.2", - "eslint": "^10.1.0", + "@typescript-eslint/parser": "^8.58.1", + "@typescript-eslint/typescript-estree": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", "eslint-visitor-keys": "^5.0.1", - "prettier": "^3.8.1", + "prettier": "^3.8.2", "regexpp": "^3.2.0", "tsutils": "^3.21.0", "typescript": "6.0.2" diff --git a/packages/router/src/binders/dispatch-on-route.binder.spec.ts b/packages/router/src/binders/dispatch-on-route.binder.spec.ts new file mode 100644 index 000000000..4b3e58c58 --- /dev/null +++ b/packages/router/src/binders/dispatch-on-route.binder.spec.ts @@ -0,0 +1,99 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { EventDispatcher } from "@ribajs/events"; +import { DispatchOnRouteBinder } from "./dispatch-on-route.binder.js"; + +/** + * Minimal Binder harness: instantiate directly, call bind/routine/unbind. + * We bypass the full view pipeline and drive the lifecycle hooks the same way + * the view does. + */ +function makeBinder(el: HTMLElement, modifier: "match" | "unmatch") { + const binder = new DispatchOnRouteBinder(); + binder.el = el; + binder.args = [modifier]; + return binder; +} + +describe("DispatchOnRouteBinder", () => { + let el: HTMLElement; + let originalPathname: string; + + beforeEach(() => { + el = document.createElement("div"); + document.body.appendChild(el); + originalPathname = window.location.pathname; + }); + + afterEach(() => { + el.remove(); + // Reset history state to avoid bleed between tests + window.history.replaceState(null, "", originalPathname); + vi.restoreAllMocks(); + }); + + it("fires `route-matched` when URL matches on initial routine", () => { + window.history.replaceState(null, "", "/docs"); + const binder = makeBinder(el, "match"); + const handler = vi.fn(); + el.addEventListener("route-matched", handler); + + binder.routine(el, "/docs"); + + expect(handler).toHaveBeenCalledTimes(1); + expect((handler.mock.calls[0][0] as CustomEvent).detail).toEqual({ + url: "/docs", + }); + }); + + it("does not fire `route-matched` when URL does not match", () => { + window.history.replaceState(null, "", "/other"); + const binder = makeBinder(el, "match"); + const handler = vi.fn(); + el.addEventListener("route-matched", handler); + + binder.routine(el, "/docs"); + + expect(handler).not.toHaveBeenCalled(); + }); + + it("fires `route-unmatched` in unmatch mode when URL does not match", () => { + window.history.replaceState(null, "", "/other"); + const binder = makeBinder(el, "unmatch"); + const handler = vi.fn(); + el.addEventListener("route-unmatched", handler); + + binder.routine(el, "/docs"); + + expect(handler).toHaveBeenCalledTimes(1); + }); + + it("re-evaluates on `newPageReady` dispatcher event", () => { + window.history.replaceState(null, "", "/other"); + const binder = makeBinder(el, "match"); + const handler = vi.fn(); + el.addEventListener("route-matched", handler); + + binder.routine(el, "/docs"); + expect(handler).not.toHaveBeenCalled(); + + window.history.replaceState(null, "", "/docs"); + EventDispatcher.getInstance("main").trigger("newPageReady"); + + expect(handler).toHaveBeenCalledTimes(1); + }); + + it("removes dispatcher listener on unbind", () => { + window.history.replaceState(null, "", "/other"); + const binder = makeBinder(el, "match"); + const handler = vi.fn(); + el.addEventListener("route-matched", handler); + + binder.routine(el, "/docs"); + binder.unbind(); + + window.history.replaceState(null, "", "/docs"); + EventDispatcher.getInstance("main").trigger("newPageReady"); + + expect(handler).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/router/src/binders/dispatch-on-route.binder.ts b/packages/router/src/binders/dispatch-on-route.binder.ts new file mode 100644 index 000000000..ea1013db6 --- /dev/null +++ b/packages/router/src/binders/dispatch-on-route.binder.ts @@ -0,0 +1,54 @@ +import { Binder } from "@ribajs/core"; +import { EventDispatcher } from "@ribajs/events"; +import { onRoute } from "@ribajs/utils/src/url.js"; + +/** + * Dispatches a CustomEvent on the bound element when the current URL matches + * (or explicitly does not match) the bound route value. + * + * Usage: + * rv-dispatch-on-route-match="'/docs'" + * → fires `route-matched` when the URL matches `/docs`. + * rv-dispatch-on-route-unmatch="'/docs'" + * → fires `route-unmatched` when the URL does not match. + * + * Combine with standard event binders to react without coupling to any service: + *
    + */ +export class DispatchOnRouteBinder extends Binder { + static key = "dispatch-on-route-*"; + + private dispatcher = EventDispatcher.getInstance("main"); + private url?: string; + private mode: "match" | "unmatch" = "match"; + + private _onNewPage() { + if (!this.url) return; + const matches = onRoute(this.url); + const shouldFire = this.mode === "match" ? matches : !matches; + if (shouldFire) { + const eventName = + this.mode === "match" ? "route-matched" : "route-unmatched"; + this.el.dispatchEvent( + new CustomEvent(eventName, { detail: { url: this.url } }), + ); + } + } + + private onNewPage = this._onNewPage.bind(this); + + unbind() { + this.dispatcher.off("newPageReady", this.onNewPage); + } + + routine(el: HTMLElement, url: string) { + if (this.args === null) { + throw new Error("args is null"); + } + this.url = url; + this.mode = (this.args[0] as "match" | "unmatch") ?? "match"; + this.dispatcher.off("newPageReady", this.onNewPage); + this.dispatcher.on("newPageReady", this.onNewPage); + this.onNewPage(); + } +} diff --git a/packages/router/src/binders/index.ts b/packages/router/src/binders/index.ts index cd0ddc8ed..c39d7270a 100644 --- a/packages/router/src/binders/index.ts +++ b/packages/router/src/binders/index.ts @@ -4,3 +4,4 @@ export * from "./route-class-star.binder.js"; export * from "./route-preload.binder.js"; export * from "./parent-route-class-star.binder.js"; export * from "./route-back-on-star.binder.js"; +export * from "./dispatch-on-route.binder.js"; diff --git a/packages/shopify-easdk/package.json b/packages/shopify-easdk/package.json index c5b7352de..8412bcbd2 100644 --- a/packages/shopify-easdk/package.json +++ b/packages/shopify-easdk/package.json @@ -38,12 +38,12 @@ "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", - "prettier": "^3.8.1", + "prettier": "^3.8.2", "typescript": "6.0.2" }, "dependencies": { diff --git a/packages/shopify-nest/package.json b/packages/shopify-nest/package.json index 06faa4e14..edc504542 100644 --- a/packages/shopify-nest/package.json +++ b/packages/shopify-nest/package.json @@ -32,8 +32,8 @@ "source": "src/index.ts", "devDependencies": { "@ribajs/tsconfig": "workspace:^", - "@types/node": "^24.12.0", - "eslint": "^10.1.0", + "@types/node": "^24.12.2", + "eslint": "^10.2.0", "typescript": "6.0.2" } } diff --git a/packages/shopify-tda/package.json b/packages/shopify-tda/package.json index 525eb64f5..3e663dedf 100644 --- a/packages/shopify-tda/package.json +++ b/packages/shopify-tda/package.json @@ -38,13 +38,13 @@ "@ribajs/eslint-config": "workspace:^", "@ribajs/tsconfig": "workspace:^", "@types/jest": "^30.0.0", - "@types/node": "^24.12.0", - "@typescript-eslint/eslint-plugin": "^8.57.2", - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^10.1.0", + "@types/node": "^24.12.2", + "@typescript-eslint/eslint-plugin": "^8.58.1", + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^10.2.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", - "prettier": "^3.8.1", + "prettier": "^3.8.2", "typescript": "6.0.2" }, "dependencies": { diff --git a/packages/shopify-tda/src/components/instagram/instagram.component.html b/packages/shopify-tda/src/components/instagram/instagram.component.html index 9110124bc..88ee6dcdf 100644 --- a/packages/shopify-tda/src/components/instagram/instagram.component.html +++ b/packages/shopify-tda/src/components/instagram/instagram.component.html @@ -1,13 +1,13 @@
    {% comment %} IMAGE {% endcomment %} - + {% comment %} CAROUSEL_ALBUM {% endcomment %} - - + + {% comment %} VIDEO {% endcomment %} -