@inglorious/logo 1.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +71 -0
  3. package/package.json +52 -42
  4. package/src/assets/faces/index.js +55 -0
  5. package/src/handlers.js +99 -0
  6. package/src/index.js +6 -0
  7. package/src/render.js +80 -0
  8. package/src/style.css +44 -0
  9. package/src/utils.js +42 -0
  10. package/src/utils.test.js +46 -0
  11. package/types/index.d.ts +45 -0
  12. package/.babelrc +0 -6
  13. package/.eslintrc.cjs +0 -33
  14. package/.storybook/main.js +0 -18
  15. package/.storybook/preview.js +0 -14
  16. package/LICENSE.md +0 -1
  17. package/dist/faces/index.js +0 -188
  18. package/dist/index.js +0 -138
  19. package/dist/logo.js +0 -95
  20. package/dist/logo.module.css +0 -43
  21. package/dist/logo.stories.js +0 -56
  22. package/src/eye.svg +0 -36
  23. package/src/faces/A.svg +0 -30
  24. package/src/faces/B.svg +0 -35
  25. package/src/faces/C.svg +0 -26
  26. package/src/faces/D.svg +0 -28
  27. package/src/faces/E.svg +0 -30
  28. package/src/faces/F.svg +0 -28
  29. package/src/faces/G.svg +0 -28
  30. package/src/faces/H.svg +0 -28
  31. package/src/faces/I.svg +0 -24
  32. package/src/faces/J.svg +0 -26
  33. package/src/faces/K.svg +0 -27
  34. package/src/faces/L.svg +0 -24
  35. package/src/faces/M.svg +0 -28
  36. package/src/faces/N.svg +0 -26
  37. package/src/faces/O.svg +0 -26
  38. package/src/faces/P.svg +0 -30
  39. package/src/faces/Q.svg +0 -33
  40. package/src/faces/R.svg +0 -32
  41. package/src/faces/S.svg +0 -30
  42. package/src/faces/T.svg +0 -26
  43. package/src/faces/U.svg +0 -24
  44. package/src/faces/V.svg +0 -23
  45. package/src/faces/W.svg +0 -28
  46. package/src/faces/X.svg +0 -28
  47. package/src/faces/Y.svg +0 -25
  48. package/src/faces/Z.svg +0 -28
  49. package/src/faces/index.js +0 -55
  50. package/src/index.jsx +0 -148
  51. package/src/logo.jsx +0 -101
  52. package/src/logo.module.css +0 -43
  53. package/src/logo.stories.jsx +0 -38
  54. package/vite.config.js +0 -8
  55. /package/{dist → src/assets}/eye.svg +0 -0
  56. /package/{dist → src/assets}/faces/A.svg +0 -0
  57. /package/{dist → src/assets}/faces/B.svg +0 -0
  58. /package/{dist → src/assets}/faces/C.svg +0 -0
  59. /package/{dist → src/assets}/faces/D.svg +0 -0
  60. /package/{dist → src/assets}/faces/E.svg +0 -0
  61. /package/{dist → src/assets}/faces/F.svg +0 -0
  62. /package/{dist → src/assets}/faces/G.svg +0 -0
  63. /package/{dist → src/assets}/faces/H.svg +0 -0
  64. /package/{dist → src/assets}/faces/I.svg +0 -0
  65. /package/{dist → src/assets}/faces/J.svg +0 -0
  66. /package/{dist → src/assets}/faces/K.svg +0 -0
  67. /package/{dist → src/assets}/faces/L.svg +0 -0
  68. /package/{dist → src/assets}/faces/M.svg +0 -0
  69. /package/{dist → src/assets}/faces/N.svg +0 -0
  70. /package/{dist → src/assets}/faces/O.svg +0 -0
  71. /package/{dist → src/assets}/faces/P.svg +0 -0
  72. /package/{dist → src/assets}/faces/Q.svg +0 -0
  73. /package/{dist → src/assets}/faces/R.svg +0 -0
  74. /package/{dist → src/assets}/faces/S.svg +0 -0
  75. /package/{dist → src/assets}/faces/T.svg +0 -0
  76. /package/{dist → src/assets}/faces/U.svg +0 -0
  77. /package/{dist → src/assets}/faces/V.svg +0 -0
  78. /package/{dist → src/assets}/faces/W.svg +0 -0
  79. /package/{dist → src/assets}/faces/X.svg +0 -0
  80. /package/{dist → src/assets}/faces/Y.svg +0 -0
  81. /package/{dist → src/assets}/faces/Z.svg +0 -0
package/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright © 2025 Inglorious Coderz Srl.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # @inglorious/logo
2
+
3
+ The Inglorious Logo component — a small, dependency-light package that provides the 3D logo render and interactive handlers for Inglorious Web.
4
+
5
+ ---
6
+
7
+ ## Install
8
+
9
+ Add the package to your project:
10
+
11
+ ```bash
12
+ pnpm add @inglorious/logo
13
+ ```
14
+
15
+ ---
16
+
17
+ ## Usage
18
+
19
+ Import the package and its stylesheet, and (optionally) the types for TypeScript.
20
+
21
+ Then, register the `logo` type in your store, create an entity of that type, and use `api.render(entityId)` inside your templates (see the [`web-logo`](https://github.com/IngloriousCoderz/inglorious-forge/tree/main/examples/apps/web-logo) demo app).
22
+
23
+ ```js
24
+ import { createStore } from "@inglorious/web"
25
+ import { logo } from "@inglorious/logo"
26
+ import "@inglorious/logo/style.css"
27
+
28
+ const entities = {
29
+ logo: {
30
+ type: "logo",
31
+ size: 256,
32
+ faces: [
33
+ { image: "I", reverse: false, eye: true },
34
+ { image: "W", reverse: false, eye: false },
35
+ ],
36
+ isInteractive: false,
37
+ isScrollPrevented: true,
38
+ },
39
+ }
40
+
41
+ const store = createStore({
42
+ types: { logo },
43
+ entities,
44
+ })
45
+
46
+ // app render function receives `api` from mount()
47
+ const app = {
48
+ render(api) {
49
+ return api.render("logo")
50
+ },
51
+ }
52
+
53
+ // TypeScript: import the entity type if you want to annotate entity shapes
54
+ // import type { LogoEntity } from "@inglorious/logo"
55
+ ```
56
+
57
+ ---
58
+
59
+ ## License
60
+
61
+ **MIT License - Free and open source**
62
+
63
+ Created by [Matteo Antony Mistretta](https://github.com/IngloriousCoderz)
64
+
65
+ You're free to use, modify, and distribute this software. See [LICENSE](./LICENSE) for details.
66
+
67
+ ---
68
+
69
+ ## Contributing
70
+
71
+ Contributions welcome! Please read our [Contributing Guidelines](../../CONTRIBUTING.md) first.
package/package.json CHANGED
@@ -1,51 +1,61 @@
1
1
  {
2
2
  "name": "@inglorious/logo",
3
- "version": "1.0.0",
4
- "private": false,
5
- "license": "SEE LICENSE IN LICENSE.md",
6
- "author": "Matteo Antony Mistretta <antony.mistretta@gmail.com> (https://www.ingloriouscoderz.it)",
7
- "main": "dist/index.js",
8
- "module": "dist/index.js",
9
- "source": "src/index.jsx",
10
- "dependencies": {
11
- "react": "^18.2.0",
12
- "react-dom": "^18.2.0"
3
+ "version": "2.0.1",
4
+ "description": "The Inglorious Coderz logo, remade to be compatible with Inglorious Web.",
5
+ "author": "IceOnFire <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/IngloriousCoderz/inglorious-forge.git",
10
+ "directory": "packages/logo"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/IngloriousCoderz/inglorious-forge/issues"
14
+ },
15
+ "keywords": [
16
+ "inglorious",
17
+ "inglorious-logo",
18
+ "logo",
19
+ "svg",
20
+ "lit-html",
21
+ "inglorious-web",
22
+ "ui",
23
+ "interactive"
24
+ ],
25
+ "type": "module",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./types/index.d.ts",
29
+ "import": "./src/index.js"
30
+ },
31
+ "./style.css": {
32
+ "import": "./src/style.css"
33
+ }
13
34
  },
14
- "peerDependencies": {
15
- "react": "^18.2.0",
16
- "react-dom": "^18.2.0"
35
+ "files": [
36
+ "src",
37
+ "types"
38
+ ],
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "dependencies": {
43
+ "@inglorious/web": "2.4.0"
17
44
  },
18
45
  "devDependencies": {
19
- "@babel/cli": "^7.23.0",
20
- "@babel/preset-env": "^7.23.2",
21
- "@babel/preset-react": "^7.22.15",
22
- "@storybook/addon-essentials": "7.4.6",
23
- "@storybook/addon-interactions": "7.4.6",
24
- "@storybook/addon-links": "7.4.6",
25
- "@storybook/addon-onboarding": "1.0.8",
26
- "@storybook/blocks": "7.4.6",
27
- "@storybook/react": "7.4.6",
28
- "@storybook/react-vite": "7.4.6",
29
- "@storybook/testing-library": "0.2.2",
30
- "babel-loader": "^9.1.3",
31
- "eslint": "^8.51.0",
32
- "eslint-plugin-react": "^7.33.2",
33
- "eslint-plugin-react-hooks": "^4.6.0",
34
- "eslint-plugin-react-refresh": "^0.4.3",
35
- "eslint-plugin-simple-import-sort": "^10.0.0",
36
- "prop-types": "15.8.1",
37
- "rimraf": "^5.0.5",
38
- "storybook": "7.4.6",
39
- "vite-plugin-svgr": "^4.1.0",
40
- "vitest": "^0.34.6"
46
+ "prettier": "^3.6.2",
47
+ "vite": "^7.1.3",
48
+ "vitest": "^4.0.15",
49
+ "@inglorious/eslint-config": "1.1.1"
50
+ },
51
+ "engines": {
52
+ "node": ">= 22"
41
53
  },
54
+ "types": "types/index.d.ts",
42
55
  "scripts": {
43
- "lint": "eslint src --fix --ext .js,.jsx",
44
- "test": "vitest",
45
- "coverage": "vitest run --coverage",
46
- "storybook": "storybook dev -p 6006",
47
- "build-storybook": "storybook build",
48
- "build": "babel src -d dist --copy-files",
49
- "prepublish": "rimraf dist && npm run build"
56
+ "format": "prettier --write '**/*.{js,jsx}'",
57
+ "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
58
+ "test:watch": "vitest",
59
+ "test": "vitest run"
50
60
  }
51
61
  }
@@ -0,0 +1,55 @@
1
+ import A from "./A.svg"
2
+ import B from "./B.svg"
3
+ import C from "./C.svg"
4
+ import D from "./D.svg"
5
+ import E from "./E.svg"
6
+ import F from "./F.svg"
7
+ import G from "./G.svg"
8
+ import H from "./H.svg"
9
+ import I from "./I.svg"
10
+ import J from "./J.svg"
11
+ import K from "./K.svg"
12
+ import L from "./L.svg"
13
+ import M from "./M.svg"
14
+ import N from "./N.svg"
15
+ import O from "./O.svg"
16
+ import P from "./P.svg"
17
+ import Q from "./Q.svg"
18
+ import R from "./R.svg"
19
+ import S from "./S.svg"
20
+ import T from "./T.svg"
21
+ import U from "./U.svg"
22
+ import V from "./V.svg"
23
+ import W from "./W.svg"
24
+ import X from "./X.svg"
25
+ import Y from "./Y.svg"
26
+ import Z from "./Z.svg"
27
+
28
+ export {
29
+ A,
30
+ B,
31
+ C,
32
+ D,
33
+ E,
34
+ F,
35
+ G,
36
+ H,
37
+ I,
38
+ J,
39
+ K,
40
+ L,
41
+ M,
42
+ N,
43
+ O,
44
+ P,
45
+ Q,
46
+ R,
47
+ S,
48
+ T,
49
+ U,
50
+ V,
51
+ W,
52
+ X,
53
+ Y,
54
+ Z,
55
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @typedef {import("./render").LogoEntity} LogoEntity
3
+ *
4
+ * @typedef {Object} Api
5
+ * @property {(id: string) => LogoEntity} getEntity
6
+ * @property {(topic: string, payload: any) => void} notify
7
+ */
8
+ import { closestAncestor, isTouchDevice, saturate } from "./utils"
9
+
10
+ const MAX_HEAD_TILT_X = 400
11
+ const MAX_HEAD_TILT_Y = 400
12
+ const HALF = 0.5
13
+ const FIRST_ITEM = 0
14
+
15
+ const eventType = isTouchDevice() ? "touchmove" : "mousemove"
16
+ let moveListener = null
17
+
18
+ export const logo = {
19
+ /**
20
+ * Initialize interactivity for the given entity.
21
+ * @param {LogoEntity} entity
22
+ * @param {string} id
23
+ * @param {Api} api
24
+ */
25
+ create(entity, id, api) {
26
+ if (id !== entity.id) return
27
+
28
+ if (!entity.isInteractive) {
29
+ return
30
+ }
31
+
32
+ startInteraction(entity, api)
33
+ },
34
+
35
+ /**
36
+ * Update the entity head tilt coordinates.
37
+ * @param {LogoEntity} entity
38
+ * @param {{x:number,y:number}} payload
39
+ */
40
+ coordsChange(entity, { x, y }) {
41
+ entity.x = x
42
+ entity.y = y
43
+ },
44
+
45
+ /**
46
+ * Cleanup listeners when the entity is destroyed.
47
+ * @param {LogoEntity} entity
48
+ * @param {string} id
49
+ */
50
+ destroy(entity, id) {
51
+ if (id !== entity.id) return
52
+
53
+ stopInteraction()
54
+ },
55
+ }
56
+
57
+ export function startInteraction(entity, api) {
58
+ moveListener = createMoveListener(entity.id, api)
59
+ document.addEventListener(eventType, moveListener, {
60
+ passive: !entity.isScrollPrevented,
61
+ })
62
+ }
63
+
64
+ export function stopInteraction() {
65
+ document.removeEventListener(eventType, moveListener)
66
+ }
67
+
68
+ /**
69
+ * Create the move listener used by `create` / `fieldChange`.
70
+ * The listener calculates the position of the pointer relative to
71
+ * the element center and notifies the entity's `coordsChange`.
72
+ *
73
+ * @param {string} entityId
74
+ * @param {Api} api
75
+ * @returns {(event:MouseEvent|Touch) => void}
76
+ */
77
+ function createMoveListener(entityId, api) {
78
+ return (event) => {
79
+ const { left, top, width, height } = event.target.getBoundingClientRect()
80
+
81
+ const center = {
82
+ x: window.scrollX + left + width * HALF,
83
+ y: window.scrollY + top + height * HALF,
84
+ }
85
+
86
+ const { target, pageX, pageY } =
87
+ eventType === "touchmove" ? event.touches[FIRST_ITEM] : event
88
+
89
+ const entity = api.getEntity(entityId)
90
+ if (entity.isScrollPrevented && closestAncestor(target, "logo")) {
91
+ event.preventDefault()
92
+ }
93
+
94
+ api.notify(`#${entityId}:coordsChange`, {
95
+ x: saturate(pageX - center.x, MAX_HEAD_TILT_X),
96
+ y: saturate(pageY - center.y, MAX_HEAD_TILT_Y),
97
+ })
98
+ }
99
+ }
package/src/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import { logo as logic } from "./handlers"
2
+ import { render } from "./render"
3
+
4
+ export const logo = { ...logic, render }
5
+
6
+ export { startInteraction, stopInteraction } from "./handlers"
package/src/render.js ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @typedef {Object} FaceSpec
3
+ * @property {string} image - svg key (A..Z)
4
+ * @property {boolean} [reverse] - whether the face image should be flipped horizontally
5
+ * @property {boolean} [eye] - whether to render the small eye asset on the face
6
+ *
7
+ * @typedef {Object} LogoEntity
8
+ * @property {string} id
9
+ * @property {number} size
10
+ * @property {number} x
11
+ * @property {number} y
12
+ * @property {[FaceSpec,FaceSpec]} faces - tuple: [leftFace, rightFace]
13
+ * @property {boolean} [isInteractive]
14
+ * @property {boolean} [isScrollPrevented]
15
+ */
16
+
17
+ import { html, styleMap, when } from "@inglorious/web"
18
+
19
+ import eye from "./assets/eye.svg"
20
+ import * as faces from "./assets/faces"
21
+
22
+ const MINUS_FORTY_DEGREES = -0.6981
23
+ const MINUS_FORTY_FIVE_DEGREES = -0.7854
24
+ const DEFAULT_SIZE = 256
25
+ const DEFAULT_COORDS = 0
26
+ const STEP = 0.001
27
+ const HALF = 0.5
28
+
29
+ /**
30
+ * Render the logo template for a `LogoEntity`.
31
+ * The returned value is a lit-html `TemplateResult` produced by `html`.
32
+ *
33
+ * @param {LogoEntity} entity
34
+ * @returns {import('lit-html').TemplateResult}
35
+ */
36
+ export function render(entity) {
37
+ const { size = DEFAULT_SIZE, x = DEFAULT_COORDS, y = DEFAULT_COORDS } = entity
38
+ const [left, right] = entity.faces
39
+
40
+ return html`<div
41
+ class="iw-logo"
42
+ style=${styleMap({
43
+ "--size": `${size}px`,
44
+ "--transform": `scaleY(1.2) translateZ(-${size}px) rotateX(${
45
+ MINUS_FORTY_DEGREES - STEP * y
46
+ }rad)
47
+ rotateY(${MINUS_FORTY_FIVE_DEGREES + STEP * x}rad)`,
48
+ "--z-translation": `${size * HALF}px`,
49
+ "--left-face-flip": left.reverse ? "rotateY(180deg)" : "none",
50
+ "--right-face-flip": right.reverse ? "rotateY(180deg)" : "none",
51
+ })}
52
+ >
53
+ <div class="iw-logo-cube">
54
+ <div class="iw-logo-cube-face left">
55
+ <img src=${faces[left.image]} alt=${left.image} />
56
+ ${when(
57
+ left.eye,
58
+ () =>
59
+ html`<img
60
+ class="iw-logo-cube-face-eye"
61
+ src=${eye}
62
+ alt="left eye"
63
+ />`,
64
+ )}
65
+ </div>
66
+ <div class="iw-logo-cube-face right">
67
+ <img src=${faces[right.image]} alt=${right.image} />
68
+ ${when(
69
+ right.eye,
70
+ () =>
71
+ html`<img
72
+ class="iw-logo-cube-face-eye"
73
+ src=${eye}
74
+ alt="right eye"
75
+ />`,
76
+ )}
77
+ </div>
78
+ </div>
79
+ </div>`
80
+ }
package/src/style.css ADDED
@@ -0,0 +1,44 @@
1
+ .iw-logo {
2
+ width: var(--size);
3
+ height: var(--size);
4
+ perspective: var(--size);
5
+ user-select: none;
6
+
7
+ > .iw-logo-cube {
8
+ height: var(--size);
9
+ transform-style: preserve-3d;
10
+ transform: var(--transform);
11
+ transition: ease-out 0.2s;
12
+
13
+ > .iw-logo-cube-face {
14
+ position: absolute;
15
+ width: 100%;
16
+ height: 100%;
17
+ transform-origin: bottom center;
18
+
19
+ > img {
20
+ position: absolute;
21
+ }
22
+
23
+ &.left {
24
+ transform: rotateY(0deg) translateZ(var(--z-translation)) skew(12deg);
25
+
26
+ > img {
27
+ transform: var(--left-face-flip);
28
+ }
29
+ }
30
+
31
+ &.right {
32
+ transform: rotateY(90deg) translateZ(var(--z-translation)) skew(-12deg);
33
+
34
+ > img {
35
+ transform: var(--right-face-flip);
36
+ }
37
+
38
+ > .iw-logo-cube-face-eye {
39
+ transform: rotateY(180deg);
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
package/src/utils.js ADDED
@@ -0,0 +1,42 @@
1
+ export function isTouchDevice() {
2
+ if (
3
+ "ontouchstart" in window ||
4
+ (window.DocumentTouch && document instanceof window.DocumentTouch)
5
+ ) {
6
+ return true
7
+ }
8
+
9
+ // include the 'heartz' as a way to have a non matching mediaQuery to help terminate the join
10
+ // https://git.io/vznFH
11
+ const prefixes = " -webkit- -moz- -o- -ms- ".split(" ")
12
+ var query = ["(", prefixes.join("touch-enabled),("), "heartz", ")"].join("")
13
+ return window.matchMedia(query).matches
14
+ }
15
+
16
+ export function saturate(num, limit) {
17
+ if (num < -limit) return -limit
18
+ if (num > limit) return limit
19
+ return num
20
+ }
21
+
22
+ export function closestAncestor(el, className) {
23
+ const limit = 4
24
+ let i = 0
25
+ let closest = el
26
+ while (closest && i < limit) {
27
+ if (
28
+ closest.className == null ||
29
+ typeof closest.className.split !== "function"
30
+ ) {
31
+ return null
32
+ }
33
+
34
+ const classes = closest.className.split(" ")
35
+ if (classes.includes(className)) {
36
+ return closest
37
+ }
38
+
39
+ closest = closest.parentNode
40
+ i++
41
+ }
42
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @vitest-environment jsdom
3
+ */
4
+ import { describe, expect, it } from "vitest"
5
+
6
+ import { closestAncestor, saturate } from "./utils.js"
7
+
8
+ describe("utils", () => {
9
+ describe("saturate", () => {
10
+ it("clamps positive values to the limit", () => {
11
+ expect(saturate(10, 5)).toBe(5)
12
+ })
13
+
14
+ it("clamps negative values to -limit", () => {
15
+ expect(saturate(-10, 5)).toBe(-5)
16
+ })
17
+
18
+ it("returns value when within limits", () => {
19
+ expect(saturate(3, 5)).toBe(3)
20
+ })
21
+ })
22
+
23
+ describe("closestAncestor", () => {
24
+ it("finds an ancestor by class name up to the search depth", () => {
25
+ const root = document.createElement("div")
26
+ root.className = "logo"
27
+
28
+ const child1 = document.createElement("div")
29
+ const child2 = document.createElement("div")
30
+ const leaf = document.createElement("span")
31
+
32
+ root.appendChild(child1)
33
+ child1.appendChild(child2)
34
+ child2.appendChild(leaf)
35
+
36
+ const found = closestAncestor(leaf, "logo")
37
+ expect(found).toBe(root)
38
+ })
39
+
40
+ it("returns null when ancestor not found or invalid className", () => {
41
+ const el = document.createElement("div")
42
+ const result = closestAncestor(el, "non-existent")
43
+ expect(result).toBeUndefined()
44
+ })
45
+ })
46
+ })
@@ -0,0 +1,45 @@
1
+ export interface Face {
2
+ /** name of the svg */
3
+ image:
4
+ | "A"
5
+ | "B"
6
+ | "C"
7
+ | "D"
8
+ | "E"
9
+ | "F"
10
+ | "G"
11
+ | "H"
12
+ | "I"
13
+ | "J"
14
+ | "K"
15
+ | "L"
16
+ | "M"
17
+ | "N"
18
+ | "O"
19
+ | "P"
20
+ | "Q"
21
+ | "R"
22
+ | "S"
23
+ | "T"
24
+ | "U"
25
+ | "V"
26
+ | "W"
27
+ | "X"
28
+ | "Y"
29
+ | "Z"
30
+ /** whether this face should be flipped horizontally */
31
+ reverse?: boolean
32
+ /** whether the small eye asset should be rendered */
33
+ eye?: boolean
34
+ }
35
+
36
+ export interface LogoEntity {
37
+ id?: string
38
+ type: string
39
+ size: number
40
+ x: number
41
+ y: number
42
+ faces: [Face, Face]
43
+ isInteractive?: boolean
44
+ isScrollPrevented?: boolean
45
+ }
package/.babelrc DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "presets": [
3
- "@babel/preset-env",
4
- ["@babel/preset-react", { "runtime": "automatic" }]
5
- ]
6
- }
package/.eslintrc.cjs DELETED
@@ -1,33 +0,0 @@
1
- module.exports = {
2
- root: true,
3
- env: { browser: true, es2020: true },
4
- extends: [
5
- "eslint:recommended",
6
- "plugin:react/recommended",
7
- "plugin:react/jsx-runtime",
8
- "plugin:react-hooks/recommended",
9
- ],
10
- ignorePatterns: ["dist", ".eslintrc.cjs"],
11
- parserOptions: { ecmaVersion: "latest", sourceType: "module" },
12
- settings: { react: { version: "18.2" } },
13
- plugins: ["react-refresh", "simple-import-sort"],
14
- rules: {
15
- "react-refresh/only-export-components": [
16
- "warn",
17
- { allowConstantExport: true },
18
- ],
19
- "no-unused-vars": 1,
20
- "no-console": [1, { allow: ["error"] }],
21
- "no-magic-numbers": 1,
22
- "simple-import-sort/imports": "warn",
23
- "simple-import-sort/exports": "warn",
24
- },
25
- overrides: [
26
- {
27
- files: "*.test.js",
28
- rules: {
29
- "no-magic-numbers": 0,
30
- },
31
- },
32
- ],
33
- };
@@ -1,18 +0,0 @@
1
- /** @type { import('@storybook/react-vite').StorybookConfig } */
2
- const config = {
3
- stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
4
- addons: [
5
- "@storybook/addon-links",
6
- "@storybook/addon-essentials",
7
- "@storybook/addon-onboarding",
8
- "@storybook/addon-interactions",
9
- ],
10
- framework: {
11
- name: "@storybook/react-vite",
12
- options: {},
13
- },
14
- docs: {
15
- autodocs: "tag",
16
- },
17
- };
18
- export default config;
@@ -1,14 +0,0 @@
1
- /** @type { import('@storybook/react').Preview } */
2
- const preview = {
3
- parameters: {
4
- actions: { argTypesRegex: "^on[A-Z].*" },
5
- controls: {
6
- matchers: {
7
- color: /(background|color)$/i,
8
- date: /Date$/,
9
- },
10
- },
11
- },
12
- };
13
-
14
- export default preview;
package/LICENSE.md DELETED
@@ -1 +0,0 @@
1
- Copyright © Inglorious Coderz 2023 All rights reserved.