@ribbon-studios/ribbon 1.0.4

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.
@@ -0,0 +1,3 @@
1
+ version: '2'
2
+ exclude_patterns:
3
+ - '**/__tests__/'
package/.editorconfig ADDED
@@ -0,0 +1,13 @@
1
+ # http://editorconfig.org
2
+ root = true
3
+
4
+ [*]
5
+ charset = utf-8
6
+ indent_style = space
7
+ indent_size = 2
8
+ end_of_line = lf
9
+ insert_final_newline = true
10
+ trim_trailing_whitespace = true
11
+
12
+ [*.md]
13
+ trim_trailing_whitespace = false
package/.eslintrc ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "parser": "@typescript-eslint/parser",
3
+ "extends": [
4
+ "eslint:recommended",
5
+ "plugin:@typescript-eslint/eslint-recommended",
6
+ "plugin:@typescript-eslint/recommended"
7
+ ],
8
+ "plugins": ["unused-imports", "@typescript-eslint"],
9
+ "rules": {
10
+ "@typescript-eslint/no-namespace": "off",
11
+ "@typescript-eslint/no-explicit-any": "off",
12
+ "unused-imports/no-unused-imports-ts": "error"
13
+ },
14
+ "ignorePatterns": ["**/node_modules/", "**/dist/", "*.d.ts"]
15
+ }
package/.gitattributes ADDED
File without changes
@@ -0,0 +1,104 @@
1
+ name: Deploy
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - main
7
+ push:
8
+ branches:
9
+ - main
10
+
11
+ jobs:
12
+ install:
13
+ name: Install
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: oven-sh/setup-bun@v1
18
+ with:
19
+ bun-version: latest
20
+
21
+ - name: Install Packages
22
+ run: bun install --frozen-lockfile
23
+
24
+ lint:
25
+ name: Lint
26
+ runs-on: ubuntu-latest
27
+ needs: [install]
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ - uses: oven-sh/setup-bun@v1
31
+ with:
32
+ bun-version: latest
33
+
34
+ - name: Install Packages
35
+ run: bun install --frozen-lockfile
36
+
37
+ - name: Lint
38
+ run: bun lint
39
+
40
+ test:
41
+ name: Test
42
+ runs-on: ubuntu-latest
43
+ needs: [install]
44
+ steps:
45
+ - uses: actions/checkout@v4
46
+ - uses: oven-sh/setup-bun@v1
47
+ with:
48
+ bun-version: latest
49
+
50
+ - name: Install Packages
51
+ run: bun install --frozen-lockfile
52
+
53
+ - name: Coverage
54
+ run: bun run test:coverage
55
+
56
+ - name: Coveralls
57
+ uses: coverallsapp/github-action@master
58
+ with:
59
+ github-token: ${{ secrets.GITHUB_TOKEN }}
60
+
61
+ build:
62
+ name: Build
63
+ runs-on: ubuntu-latest
64
+ needs: [install]
65
+ steps:
66
+ - uses: actions/checkout@v4
67
+ - uses: oven-sh/setup-bun@v1
68
+ with:
69
+ bun-version: latest
70
+
71
+ - name: Install Packages
72
+ run: bun install --frozen-lockfile
73
+
74
+ - name: Build
75
+ run: bun run build
76
+
77
+ - uses: actions/upload-artifact@v2
78
+ with:
79
+ name: build
80
+ path: dist
81
+
82
+ deploy:
83
+ name: Deploy
84
+ runs-on: ubuntu-latest
85
+ needs: [lint, test, build]
86
+ steps:
87
+ - uses: actions/checkout@v4
88
+ - uses: oven-sh/setup-bun@v1
89
+ with:
90
+ bun-version: latest
91
+
92
+ - name: Install Packages
93
+ run: bun install --frozen-lockfile
94
+
95
+ - uses: actions/download-artifact@v3
96
+ with:
97
+ name: build
98
+ path: dist
99
+
100
+ - name: Deploy
101
+ run: bunx semantic-release@^22
102
+ env:
103
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
104
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 18
package/.prettierrc ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "trailingComma": "es5",
3
+ "semi": true,
4
+ "singleQuote": true,
5
+ "editorconfig": true,
6
+ "printWidth": 120
7
+ }
package/.releaserc.yml ADDED
@@ -0,0 +1,2 @@
1
+ branches:
2
+ - main
@@ -0,0 +1,24 @@
1
+ {
2
+ "typescript.preferences.quoteStyle": "single",
3
+ "javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false,
4
+ "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false,
5
+ "editor.formatOnSave": true,
6
+ "editor.codeActionsOnSave": {
7
+ "source.fixAll.eslint": "explicit"
8
+ },
9
+ "json.schemas": [
10
+ {
11
+ "url": "https://json.schemastore.org/prettierrc.json",
12
+ "fileMatch": [".prettierrc"]
13
+ },
14
+ {
15
+ "url": "https://json.schemastore.org/web-manifest-combined.json",
16
+ "fileMatch": ["site.webmanifest"]
17
+ }
18
+ ],
19
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
20
+ "editor.formatOnPaste": false,
21
+ "editor.formatOnType": false,
22
+ "editor.formatOnSaveMode": "file",
23
+ "files.autoSave": "onFocusChange"
24
+ }
@@ -0,0 +1,22 @@
1
+ ### 🤖 Contributing to Ribbon 💗
2
+
3
+ See an [issue](/issues) or need that you're able to fulfill?
4
+ Then this is the perfect place for you!
5
+
6
+ ## Prerequisites
7
+
8
+ - NodeJS 18
9
+
10
+ ## Setting Up Locally
11
+
12
+ - Install the Dependencies
13
+
14
+ ```sh
15
+ $ bun install
16
+ ```
17
+
18
+ ## Run the Tests~
19
+
20
+ ```sh
21
+ $ bun run test
22
+ ```
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ [![NPM Version][npm-version-image]][npm-url]
2
+ [![NPM Downloads][npm-downloads-image]][npm-url]
3
+ [![Coveralls][coveralls-image]][coveralls-url]
4
+
5
+ [![CI Build][github-actions-image]][github-actions-url]
6
+ [![Maintainability][maintainability-image]][maintainability-url]
7
+ [![Semantic Release][semantic-release-image]][semantic-release-url]
8
+ [![Code Style: Prettier][code-style-image]][code-style-url]
9
+
10
+ # `@ribbon-studios/ribbon`
11
+
12
+ ### Preview
13
+
14
+ ![Example](https://github.com/ribbon-studios/ribbon/assets/9692284/5ee5e4b3-c676-4ee5-9ec4-ae7f52fd4640)
15
+
16
+ ### Usage
17
+
18
+ ```tsx
19
+ import { Ribbon } from '@ribbon-studios/ribbon';
20
+
21
+ export function MyApp() {
22
+ return (
23
+ <div>
24
+ My super cool content.
25
+ <Ribbon>Local</Ribbon>
26
+ </div>
27
+ );
28
+ }
29
+ ```
30
+
31
+ ### Properties
32
+
33
+ | Name | Description | Optional? |
34
+ | --------------- | ----------------------------- | ----------------------------- |
35
+ | children | The contents of the ribbon | Yes |
36
+ | color | The font color | Yes (defaults to `black`) |
37
+ | backgroundColor | The background color | Yes (defaults to `white`) |
38
+ | position | The positioning of the ribbon | Yes (defaults to `top-right`) |
39
+
40
+ [_**Want to Contribute?**_](/CONTRIBUTING.md)
41
+
42
+ [npm-version-image]: https://img.shields.io/npm/v/@ribbon-studios/ribbon.svg
43
+ [npm-downloads-image]: https://img.shields.io/npm/dm/@ribbon-studios/ribbon.svg
44
+ [npm-url]: https://npmjs.org/package/@ribbon-studios/ribbon
45
+ [github-actions-image]: https://img.shields.io/github/actions/workflow/status/ribbon-studios/ribbon/ci.yml?event=push
46
+ [github-actions-url]: https://github.com/ribbon-studios/ribbon/actions/workflows/ci.yml?query=branch%3Amain
47
+ [coveralls-image]: https://img.shields.io/coveralls/ribbon-studios/ribbon.svg
48
+ [coveralls-url]: https://coveralls.io/github/ribbon-studios/ribbon?branch=main
49
+ [code-style-image]: https://img.shields.io/badge/code%20style-prettier-ff69b4.svg
50
+ [code-style-url]: https://prettier.io
51
+ [maintainability-image]: https://img.shields.io/codeclimate/maintainability/ribbon-studios/ribbon
52
+ [maintainability-url]: https://codeclimate.com/github/ribbon-studios/ribbon/maintainability
53
+ [semantic-release-url]: https://github.com/semantic-release/semantic-release
54
+ [semantic-release-image]: https://img.shields.io/badge/%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079
@@ -0,0 +1,13 @@
1
+ // identity-obj-proxy has a bug when using scss files with the syntax '* as styles'
2
+ module.exports = new Proxy(
3
+ {},
4
+ {
5
+ get: function getter(target, key) {
6
+ if (key === '__esModule') {
7
+ // True instead of false to pretend we're an ES module.
8
+ return true;
9
+ }
10
+ return key;
11
+ },
12
+ }
13
+ );
package/bun.lockb ADDED
Binary file
package/demo/App.tsx ADDED
@@ -0,0 +1,44 @@
1
+ import { useReadOnlyCachedState } from '@ribbon-studios/react-utils';
2
+ import { Ribbon } from '../src';
3
+ import { useState } from 'react';
4
+
5
+ type Banner = {
6
+ label: string;
7
+ backgroundColor: string;
8
+ color?: string;
9
+ };
10
+
11
+ const banners: Banner[] = [
12
+ {
13
+ label: 'local',
14
+ backgroundColor: '#66BB6A',
15
+ },
16
+ {
17
+ label: 'review',
18
+ backgroundColor: '#7B1FA2',
19
+ color: 'white',
20
+ },
21
+ {
22
+ label: 'alpha',
23
+ backgroundColor: '#2196F3',
24
+ },
25
+ ];
26
+
27
+ export function App() {
28
+ const [index, setIndex] = useState(0);
29
+ const banner = useReadOnlyCachedState(() => {
30
+ return banners[index];
31
+ }, [index]);
32
+
33
+ return (
34
+ <div>
35
+ <Ribbon
36
+ backgroundColor={banner.backgroundColor}
37
+ color={banner.color}
38
+ onClick={() => setIndex((index + 1) % banners.length)}
39
+ >
40
+ {banner.label}
41
+ </Ribbon>
42
+ </div>
43
+ );
44
+ }
@@ -0,0 +1,19 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Ribbon Demo</title>
7
+
8
+ <style>
9
+ body {
10
+ background-color: #272727;
11
+ }
12
+ </style>
13
+ </head>
14
+ <body>
15
+ <div id="root"></div>
16
+
17
+ <script type="module" src="./index.tsx"></script>
18
+ </body>
19
+ </html>
package/demo/index.tsx ADDED
@@ -0,0 +1,4 @@
1
+ import { createRoot } from 'react-dom/client';
2
+ import { App } from './App';
3
+
4
+ createRoot(document.getElementById('root') as HTMLElement).render(<App />);
package/dist/index.css ADDED
@@ -0,0 +1,55 @@
1
+ .Z10rOW_ribbon {
2
+ z-index: 9999;
3
+ pointer-events: none;
4
+ -webkit-user-select: none;
5
+ user-select: none;
6
+ opacity: 1;
7
+ backface-visibility: hidden;
8
+ width: 80px;
9
+ height: 80px;
10
+ transition: opacity .15s ease-in-out;
11
+ position: fixed;
12
+ transform: rotate(45deg);
13
+ }
14
+
15
+ .Z10rOW_ribbon:hover {
16
+ opacity: .75;
17
+ }
18
+
19
+ .Z10rOW_content {
20
+ text-align: center;
21
+ pointer-events: all;
22
+ text-transform: uppercase;
23
+ color: #333;
24
+ background-color: #fafafa;
25
+ width: 150px;
26
+ padding: 7px 0;
27
+ font: bold 15px sans-serif;
28
+ position: relative;
29
+ top: 50%;
30
+ left: 50%;
31
+ transform: translate(-50%, -50%);
32
+ box-shadow: 0 0 3px #0000004d;
33
+ }
34
+
35
+ .Z10rOW_ribbon.Z10rOW_bottom.Z10rOW_right, .Z10rOW_ribbon.Z10rOW_top.Z10rOW_left {
36
+ transform: rotate(-45deg);
37
+ }
38
+
39
+ .Z10rOW_ribbon.Z10rOW_bottom {
40
+ bottom: -10px;
41
+ }
42
+
43
+ .Z10rOW_ribbon.Z10rOW_top {
44
+ top: -10px;
45
+ }
46
+
47
+ .Z10rOW_ribbon.Z10rOW_left {
48
+ left: -10px;
49
+ }
50
+
51
+ .Z10rOW_ribbon.Z10rOW_right {
52
+ right: -10px;
53
+ }
54
+
55
+ /*# sourceMappingURL=index.css.map */
@@ -0,0 +1 @@
1
+ {"mappings":"AAAA;;;;;;;;;;;;;;AAaA;;;;AAIA;;;;;;;;;;;;;;;;AAgBA;;;;AAIA;;;;AAGA;;;;AAGA;;;;AAGA","sources":["src/Ribbon.module.scss"],"sourcesContent":[".ribbon {\n position: fixed;\n z-index: 9999;\n width: 80px;\n height: 80px;\n pointer-events: none;\n user-select: none;\n opacity: 1;\n transform: rotate(45deg);\n transition: opacity 0.15s ease-in-out;\n backface-visibility: hidden;\n}\n\n.ribbon:hover {\n opacity: 0.75;\n}\n\n.content {\n font: bold 15px Sans-Serif;\n text-align: center;\n position: relative;\n padding: 7px 0;\n width: 150px;\n box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.3);\n pointer-events: all;\n text-transform: uppercase;\n transform: translate(-50%, -50%);\n left: 50%;\n top: 50%;\n background-color: #fafafa;\n color: #333;\n}\n\n.ribbon.bottom.right,\n.ribbon.top.left {\n transform: rotate(-45deg);\n}\n.ribbon.bottom {\n bottom: -10px;\n}\n.ribbon.top {\n top: -10px;\n}\n.ribbon.left {\n left: -10px;\n}\n.ribbon.right {\n right: -10px;\n}\n"],"names":[],"version":3,"file":"index.css.map"}
package/dist/index.js ADDED
@@ -0,0 +1,75 @@
1
+ require("./index.css");
2
+ var $4Xqol$reactjsxruntime = require("react/jsx-runtime");
3
+
4
+
5
+ function $parcel$exportWildcard(dest, source) {
6
+ Object.keys(source).forEach(function(key) {
7
+ if (key === 'default' || key === '__esModule' || Object.prototype.hasOwnProperty.call(dest, key)) {
8
+ return;
9
+ }
10
+
11
+ Object.defineProperty(dest, key, {
12
+ enumerable: true,
13
+ get: function get() {
14
+ return source[key];
15
+ }
16
+ });
17
+ });
18
+
19
+ return dest;
20
+ }
21
+
22
+ function $parcel$export(e, n, v, s) {
23
+ Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
24
+ }
25
+ var $75b00914025482a3$exports = {};
26
+
27
+ $parcel$export($75b00914025482a3$exports, "Ribbon", function () { return $75b00914025482a3$export$6acf8bfe08d451ac; });
28
+ // eslint-disable-next-line @typescript-eslint/triple-slash-reference
29
+ /// <reference path="../global.d.ts" />
30
+
31
+ var $e02b4baa154171cc$exports = {};
32
+
33
+ $parcel$export($e02b4baa154171cc$exports, "bottom", function () { return $e02b4baa154171cc$export$40e543e69a8b3fbb; }, function (v) { return $e02b4baa154171cc$export$40e543e69a8b3fbb = v; });
34
+ $parcel$export($e02b4baa154171cc$exports, "content", function () { return $e02b4baa154171cc$export$a7db06668cad9adb; }, function (v) { return $e02b4baa154171cc$export$a7db06668cad9adb = v; });
35
+ $parcel$export($e02b4baa154171cc$exports, "left", function () { return $e02b4baa154171cc$export$eabcd2c8791e7bf4; }, function (v) { return $e02b4baa154171cc$export$eabcd2c8791e7bf4 = v; });
36
+ $parcel$export($e02b4baa154171cc$exports, "ribbon", function () { return $e02b4baa154171cc$export$b463d277d30b81dc; }, function (v) { return $e02b4baa154171cc$export$b463d277d30b81dc = v; });
37
+ $parcel$export($e02b4baa154171cc$exports, "right", function () { return $e02b4baa154171cc$export$79ffe56a765070d2; }, function (v) { return $e02b4baa154171cc$export$79ffe56a765070d2 = v; });
38
+ $parcel$export($e02b4baa154171cc$exports, "top", function () { return $e02b4baa154171cc$export$1e95b668f3b82d; }, function (v) { return $e02b4baa154171cc$export$1e95b668f3b82d = v; });
39
+ var $e02b4baa154171cc$export$40e543e69a8b3fbb;
40
+ var $e02b4baa154171cc$export$a7db06668cad9adb;
41
+ var $e02b4baa154171cc$export$eabcd2c8791e7bf4;
42
+ var $e02b4baa154171cc$export$b463d277d30b81dc;
43
+ var $e02b4baa154171cc$export$79ffe56a765070d2;
44
+ var $e02b4baa154171cc$export$1e95b668f3b82d;
45
+ $e02b4baa154171cc$export$40e543e69a8b3fbb = `Z10rOW_bottom`;
46
+ $e02b4baa154171cc$export$a7db06668cad9adb = `Z10rOW_content`;
47
+ $e02b4baa154171cc$export$eabcd2c8791e7bf4 = `Z10rOW_left`;
48
+ $e02b4baa154171cc$export$b463d277d30b81dc = `Z10rOW_ribbon`;
49
+ $e02b4baa154171cc$export$79ffe56a765070d2 = `Z10rOW_right`;
50
+ $e02b4baa154171cc$export$1e95b668f3b82d = `Z10rOW_top`;
51
+
52
+
53
+ function $75b00914025482a3$export$6acf8bfe08d451ac({ children: children, backgroundColor: backgroundColor = "white", color: color = "black", position: position = "top-right", ...props }) {
54
+ const [vertical, horizontal] = position.split("-");
55
+ return /*#__PURE__*/ (0, $4Xqol$reactjsxruntime.jsx)("div", {
56
+ ...props,
57
+ className: `${$e02b4baa154171cc$exports.ribbon} ${$e02b4baa154171cc$exports[vertical]} ${$e02b4baa154171cc$exports[horizontal]}`,
58
+ "data-testid": "ribbon",
59
+ children: /*#__PURE__*/ (0, $4Xqol$reactjsxruntime.jsx)("div", {
60
+ className: $e02b4baa154171cc$exports.content,
61
+ style: {
62
+ backgroundColor: backgroundColor,
63
+ color: color
64
+ },
65
+ "data-testid": "ribbon-content",
66
+ children: children
67
+ })
68
+ });
69
+ }
70
+
71
+
72
+ $parcel$exportWildcard(module.exports, $75b00914025482a3$exports);
73
+
74
+
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;ACAA,qEAAqE;AACrE,uCAAuC;;;;;;;;;;ACDvC,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AALA,4CAA2B,CAAC,aAAa,CAAC;AAC1C,4CAA4B,CAAC,cAAc,CAAC;AAC5C,4CAAyB,CAAC,WAAW,CAAC;AACtC,4CAA2B,CAAC,aAAa,CAAC;AAC1C,4CAA0B,CAAC,YAAY,CAAC;AACxC,0CAAwB,CAAC,UAAU,CAAC;;;AD6B7B,SAAS,0CAAO,YACrB,QAAQ,mBACR,kBAAkB,gBAClB,QAAQ,mBACR,WAAW,aACX,GAAG,OACS;IACZ,MAAM,CAAC,UAAU,WAAW,GAAG,SAAS,KAAK,CAAC;IAE9C,qBACE,gCAAC;QAAK,GAAG,KAAK;QAAE,WAAW,CAAC,EAAE,0BAAO,MAAM,CAAC,CAAC,EAAE,yBAAM,CAAC,SAAS,CAAC,CAAC,EAAE,yBAAM,CAAC,WAAW,CAAC,CAAC;QAAE,eAAY;kBACnG,cAAA,gCAAC;YAAI,WAAW,0BAAO,OAAO;YAAE,OAAO;iCAAE;uBAAiB;YAAM;YAAG,eAAY;sBAC5E;;;AAIT","sources":["src/index.ts","src/Ribbon.tsx","src/Ribbon.module.scss"],"sourcesContent":["export * from './Ribbon';\n","// eslint-disable-next-line @typescript-eslint/triple-slash-reference\n/// <reference path=\"../global.d.ts\" />\n\nimport { ComponentProps, ReactNode } from 'react';\nimport * as styles from './Ribbon.module.scss';\n\nexport type RibbonProps = {\n /**\n * The contents of the ribbon\n */\n children?: ReactNode;\n\n /**\n * The font color\n *\n * @default 'black'\n */\n color?: string;\n\n /**\n * The background color\n *\n * @default 'white'\n */\n backgroundColor?: string;\n\n /**\n * Whether the ribbon should be aligned at the 'top' or 'bottom'\n *\n * @default 'top-right'\n */\n position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';\n} & Pick<ComponentProps<'div'>, 'onClick'>;\n\nexport function Ribbon({\n children,\n backgroundColor = 'white',\n color = 'black',\n position = 'top-right',\n ...props\n}: RibbonProps) {\n const [vertical, horizontal] = position.split('-');\n\n return (\n <div {...props} className={`${styles.ribbon} ${styles[vertical]} ${styles[horizontal]}`} data-testid=\"ribbon\">\n <div className={styles.content} style={{ backgroundColor, color }} data-testid=\"ribbon-content\">\n {children}\n </div>\n </div>\n );\n}\n",".ribbon {\n position: fixed;\n z-index: 9999;\n width: 80px;\n height: 80px;\n pointer-events: none;\n user-select: none;\n opacity: 1;\n transform: rotate(45deg);\n transition: opacity 0.15s ease-in-out;\n backface-visibility: hidden;\n}\n\n.ribbon:hover {\n opacity: 0.75;\n}\n\n.content {\n font: bold 15px Sans-Serif;\n text-align: center;\n position: relative;\n padding: 7px 0;\n width: 150px;\n box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.3);\n pointer-events: all;\n text-transform: uppercase;\n transform: translate(-50%, -50%);\n left: 50%;\n top: 50%;\n background-color: #fafafa;\n color: #333;\n}\n\n.ribbon.bottom.right,\n.ribbon.top.left {\n transform: rotate(-45deg);\n}\n.ribbon.bottom {\n bottom: -10px;\n}\n.ribbon.top {\n top: -10px;\n}\n.ribbon.left {\n left: -10px;\n}\n.ribbon.right {\n right: -10px;\n}\n"],"names":[],"version":3,"file":"index.js.map"}
package/dist/module.js ADDED
@@ -0,0 +1,58 @@
1
+ import "./index.css";
2
+ import {jsx as $lkOeU$jsx} from "react/jsx-runtime";
3
+
4
+
5
+ function $parcel$export(e, n, v, s) {
6
+ Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
7
+ }
8
+ var $05af965021a88cfe$exports = {};
9
+
10
+ $parcel$export($05af965021a88cfe$exports, "Ribbon", function () { return $05af965021a88cfe$export$6acf8bfe08d451ac; });
11
+ // eslint-disable-next-line @typescript-eslint/triple-slash-reference
12
+ /// <reference path="../global.d.ts" />
13
+
14
+ var $ba41f8ebd12a42a2$exports = {};
15
+
16
+ $parcel$export($ba41f8ebd12a42a2$exports, "bottom", function () { return $ba41f8ebd12a42a2$export$40e543e69a8b3fbb; }, function (v) { return $ba41f8ebd12a42a2$export$40e543e69a8b3fbb = v; });
17
+ $parcel$export($ba41f8ebd12a42a2$exports, "content", function () { return $ba41f8ebd12a42a2$export$a7db06668cad9adb; }, function (v) { return $ba41f8ebd12a42a2$export$a7db06668cad9adb = v; });
18
+ $parcel$export($ba41f8ebd12a42a2$exports, "left", function () { return $ba41f8ebd12a42a2$export$eabcd2c8791e7bf4; }, function (v) { return $ba41f8ebd12a42a2$export$eabcd2c8791e7bf4 = v; });
19
+ $parcel$export($ba41f8ebd12a42a2$exports, "ribbon", function () { return $ba41f8ebd12a42a2$export$b463d277d30b81dc; }, function (v) { return $ba41f8ebd12a42a2$export$b463d277d30b81dc = v; });
20
+ $parcel$export($ba41f8ebd12a42a2$exports, "right", function () { return $ba41f8ebd12a42a2$export$79ffe56a765070d2; }, function (v) { return $ba41f8ebd12a42a2$export$79ffe56a765070d2 = v; });
21
+ $parcel$export($ba41f8ebd12a42a2$exports, "top", function () { return $ba41f8ebd12a42a2$export$1e95b668f3b82d; }, function (v) { return $ba41f8ebd12a42a2$export$1e95b668f3b82d = v; });
22
+ var $ba41f8ebd12a42a2$export$40e543e69a8b3fbb;
23
+ var $ba41f8ebd12a42a2$export$a7db06668cad9adb;
24
+ var $ba41f8ebd12a42a2$export$eabcd2c8791e7bf4;
25
+ var $ba41f8ebd12a42a2$export$b463d277d30b81dc;
26
+ var $ba41f8ebd12a42a2$export$79ffe56a765070d2;
27
+ var $ba41f8ebd12a42a2$export$1e95b668f3b82d;
28
+ $ba41f8ebd12a42a2$export$40e543e69a8b3fbb = `Z10rOW_bottom`;
29
+ $ba41f8ebd12a42a2$export$a7db06668cad9adb = `Z10rOW_content`;
30
+ $ba41f8ebd12a42a2$export$eabcd2c8791e7bf4 = `Z10rOW_left`;
31
+ $ba41f8ebd12a42a2$export$b463d277d30b81dc = `Z10rOW_ribbon`;
32
+ $ba41f8ebd12a42a2$export$79ffe56a765070d2 = `Z10rOW_right`;
33
+ $ba41f8ebd12a42a2$export$1e95b668f3b82d = `Z10rOW_top`;
34
+
35
+
36
+ function $05af965021a88cfe$export$6acf8bfe08d451ac({ children: children, backgroundColor: backgroundColor = "white", color: color = "black", position: position = "top-right", ...props }) {
37
+ const [vertical, horizontal] = position.split("-");
38
+ return /*#__PURE__*/ (0, $lkOeU$jsx)("div", {
39
+ ...props,
40
+ className: `${$ba41f8ebd12a42a2$exports.ribbon} ${$ba41f8ebd12a42a2$exports[vertical]} ${$ba41f8ebd12a42a2$exports[horizontal]}`,
41
+ "data-testid": "ribbon",
42
+ children: /*#__PURE__*/ (0, $lkOeU$jsx)("div", {
43
+ className: $ba41f8ebd12a42a2$exports.content,
44
+ style: {
45
+ backgroundColor: backgroundColor,
46
+ color: color
47
+ },
48
+ "data-testid": "ribbon-content",
49
+ children: children
50
+ })
51
+ });
52
+ }
53
+
54
+
55
+
56
+
57
+ export {$05af965021a88cfe$export$6acf8bfe08d451ac as Ribbon};
58
+ //# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
1
+ {"mappings":";;;;;;;;;;ACAA,qEAAqE;AACrE,uCAAuC;;;;;;;;;;ACDvC,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AALA,4CAA2B,CAAC,aAAa,CAAC;AAC1C,4CAA4B,CAAC,cAAc,CAAC;AAC5C,4CAAyB,CAAC,WAAW,CAAC;AACtC,4CAA2B,CAAC,aAAa,CAAC;AAC1C,4CAA0B,CAAC,YAAY,CAAC;AACxC,0CAAwB,CAAC,UAAU,CAAC;;;AD6B7B,SAAS,0CAAO,YACrB,QAAQ,mBACR,kBAAkB,gBAClB,QAAQ,mBACR,WAAW,aACX,GAAG,OACS;IACZ,MAAM,CAAC,UAAU,WAAW,GAAG,SAAS,KAAK,CAAC;IAE9C,qBACE,gBAAC;QAAK,GAAG,KAAK;QAAE,WAAW,CAAC,EAAE,0BAAO,MAAM,CAAC,CAAC,EAAE,yBAAM,CAAC,SAAS,CAAC,CAAC,EAAE,yBAAM,CAAC,WAAW,CAAC,CAAC;QAAE,eAAY;kBACnG,cAAA,gBAAC;YAAI,WAAW,0BAAO,OAAO;YAAE,OAAO;iCAAE;uBAAiB;YAAM;YAAG,eAAY;sBAC5E;;;AAIT","sources":["src/index.ts","src/Ribbon.tsx","src/Ribbon.module.scss"],"sourcesContent":["export * from './Ribbon';\n","// eslint-disable-next-line @typescript-eslint/triple-slash-reference\n/// <reference path=\"../global.d.ts\" />\n\nimport { ComponentProps, ReactNode } from 'react';\nimport * as styles from './Ribbon.module.scss';\n\nexport type RibbonProps = {\n /**\n * The contents of the ribbon\n */\n children?: ReactNode;\n\n /**\n * The font color\n *\n * @default 'black'\n */\n color?: string;\n\n /**\n * The background color\n *\n * @default 'white'\n */\n backgroundColor?: string;\n\n /**\n * Whether the ribbon should be aligned at the 'top' or 'bottom'\n *\n * @default 'top-right'\n */\n position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';\n} & Pick<ComponentProps<'div'>, 'onClick'>;\n\nexport function Ribbon({\n children,\n backgroundColor = 'white',\n color = 'black',\n position = 'top-right',\n ...props\n}: RibbonProps) {\n const [vertical, horizontal] = position.split('-');\n\n return (\n <div {...props} className={`${styles.ribbon} ${styles[vertical]} ${styles[horizontal]}`} data-testid=\"ribbon\">\n <div className={styles.content} style={{ backgroundColor, color }} data-testid=\"ribbon-content\">\n {children}\n </div>\n </div>\n );\n}\n",".ribbon {\n position: fixed;\n z-index: 9999;\n width: 80px;\n height: 80px;\n pointer-events: none;\n user-select: none;\n opacity: 1;\n transform: rotate(45deg);\n transition: opacity 0.15s ease-in-out;\n backface-visibility: hidden;\n}\n\n.ribbon:hover {\n opacity: 0.75;\n}\n\n.content {\n font: bold 15px Sans-Serif;\n text-align: center;\n position: relative;\n padding: 7px 0;\n width: 150px;\n box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.3);\n pointer-events: all;\n text-transform: uppercase;\n transform: translate(-50%, -50%);\n left: 50%;\n top: 50%;\n background-color: #fafafa;\n color: #333;\n}\n\n.ribbon.bottom.right,\n.ribbon.top.left {\n transform: rotate(-45deg);\n}\n.ribbon.bottom {\n bottom: -10px;\n}\n.ribbon.top {\n top: -10px;\n}\n.ribbon.left {\n left: -10px;\n}\n.ribbon.right {\n right: -10px;\n}\n"],"names":[],"version":3,"file":"module.js.map"}
@@ -0,0 +1,28 @@
1
+ import { ComponentProps, ReactNode } from "react";
2
+ export type RibbonProps = {
3
+ /**
4
+ * The contents of the ribbon
5
+ */
6
+ children?: ReactNode;
7
+ /**
8
+ * The font color
9
+ *
10
+ * @default 'black'
11
+ */
12
+ color?: string;
13
+ /**
14
+ * The background color
15
+ *
16
+ * @default 'white'
17
+ */
18
+ backgroundColor?: string;
19
+ /**
20
+ * Whether the ribbon should be aligned at the 'top' or 'bottom'
21
+ *
22
+ * @default 'top-right'
23
+ */
24
+ position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
25
+ } & Pick<ComponentProps<'div'>, 'onClick'>;
26
+ export function Ribbon({ children, backgroundColor, color, position, ...props }: RibbonProps): import("react/jsx-runtime").JSX.Element;
27
+
28
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"mappings":";AAMA,0BAA0B;IACxB;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,cAAc,GAAG,aAAa,CAAC;CACtE,GAAG,IAAI,CAAC,eAAe,KAAK,CAAC,EAAE,SAAS,CAAC,CAAC;AAE3C,uBAAuB,EACrB,QAAQ,EACR,eAAyB,EACzB,KAAe,EACf,QAAsB,EACtB,GAAG,KAAK,EACT,EAAE,WAAW,2CAUb","sources":["src/src/Ribbon.tsx","src/src/index.ts","src/index.ts"],"sourcesContent":[null,null,"export * from './Ribbon';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
package/flake.lock ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "nodes": {
3
+ "nixpkgs": {
4
+ "locked": {
5
+ "lastModified": 1712608508,
6
+ "narHash": "sha256-vMZ5603yU0wxgyQeHJryOI+O61yrX2AHwY6LOFyV1gM=",
7
+ "owner": "NixOS",
8
+ "repo": "nixpkgs",
9
+ "rev": "4cba8b53da471aea2ab2b0c1f30a81e7c451f4b6",
10
+ "type": "github"
11
+ },
12
+ "original": {
13
+ "owner": "NixOS",
14
+ "ref": "nixos-unstable",
15
+ "repo": "nixpkgs",
16
+ "type": "github"
17
+ }
18
+ },
19
+ "root": {
20
+ "inputs": {
21
+ "nixpkgs": "nixpkgs"
22
+ }
23
+ }
24
+ },
25
+ "root": "root",
26
+ "version": 7
27
+ }
package/flake.nix ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ description = "Ribbon";
3
+
4
+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
5
+
6
+ outputs = { nixpkgs, ... }: let
7
+ forAllSystems = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed;
8
+ in {
9
+ # Devshell for bootstrapping; acessible via 'nix develop' or 'nix-shell' (legacy)
10
+ devShells = forAllSystems (systems:
11
+ let pkgs = nixpkgs.legacyPackages.${systems};
12
+ in import ./shell.nix { inherit pkgs; }
13
+ );
14
+ };
15
+ }
package/global.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ declare module '*.module.scss' {
2
+ const content: Record<string, string>;
3
+ export = content;
4
+ export default content;
5
+ }
package/jest.config.ts ADDED
@@ -0,0 +1,30 @@
1
+ import type { JestConfigWithTsJest } from 'ts-jest';
2
+
3
+ const jestConfig: JestConfigWithTsJest = {
4
+ roots: ['<rootDir>/src'],
5
+ testEnvironment: 'jsdom',
6
+
7
+ moduleFileExtensions: ['js', 'mjs', 'cjs', 'jsx', 'ts', 'd.ts', 'tsx', 'json', 'node'],
8
+
9
+ transform: {
10
+ '^.+\\.tsx?$': [
11
+ 'ts-jest',
12
+ {
13
+ tsconfig: 'tsconfig.json',
14
+ },
15
+ ],
16
+ },
17
+
18
+ coverageProvider: 'v8',
19
+ collectCoverageFrom: ['<rootDir>/src/**/*'],
20
+
21
+ coveragePathIgnorePatterns: ['__tests__'],
22
+
23
+ moduleNameMapper: {
24
+ '\\.(scss)$': '<rootDir>/__tests__/identity-obj-proxy-esm.ts',
25
+ },
26
+
27
+ transformIgnorePatterns: ['^.+\\.js$'],
28
+ };
29
+
30
+ export default jestConfig;
package/nixpkgs.nix ADDED
@@ -0,0 +1,8 @@
1
+ # A nixpkgs instance that is grabbed from the pinned nixpkgs commit in the lock file
2
+ # Useful to avoid using channels when using legacy nix commands
3
+ let lock = (builtins.fromJSON (builtins.readFile ./flake.lock)).nodes.nixpkgs.locked;
4
+ in
5
+ import (fetchTarball {
6
+ url = "https://github.com/nixos/nixpkgs/archive/${lock.rev}.tar.gz";
7
+ sha256 = lock.narHash;
8
+ })
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@ribbon-studios/ribbon",
3
+ "description": "A simple react ribbon~",
4
+ "version": "1.0.4",
5
+ "source": "src/index.ts",
6
+ "main": "dist/index.js",
7
+ "module": "dist/module.js",
8
+ "types": "dist/types.d.ts",
9
+ "scripts": {
10
+ "start": "parcel demo/index.html",
11
+ "build": "parcel build",
12
+ "lint": "eslint .",
13
+ "test": "jest --coverage=false",
14
+ "test:coverage": "jest --coverage"
15
+ },
16
+ "peerDependencies": {
17
+ "react": "*"
18
+ },
19
+ "devDependencies": {
20
+ "@kayahr/jest-electron-runner": "^29.11.0",
21
+ "@parcel/packager-ts": "2.11.0",
22
+ "@parcel/transformer-inline-string": "2.11.0",
23
+ "@parcel/transformer-sass": "2.11.0",
24
+ "@parcel/transformer-typescript-types": "2.11.0",
25
+ "@testing-library/react": "^14.1.2",
26
+ "@types/chance": "^1.1.6",
27
+ "@types/jest": "^29.5.11",
28
+ "@types/react": "^18.2.47",
29
+ "@types/react-dom": "^18.2.18",
30
+ "@typescript-eslint/eslint-plugin": "^6.18.1",
31
+ "@typescript-eslint/parser": "^6.18.1",
32
+ "chance": "^1.1.11",
33
+ "eslint": "^8.56.0",
34
+ "eslint-plugin-unused-imports": "^3.0.0",
35
+ "jest": "^29.7.0",
36
+ "jest-environment-jsdom": "^29.7.0",
37
+ "parcel": "^2.11.0",
38
+ "process": "^0.11.10",
39
+ "react": "^18.2.0",
40
+ "react-dom": "^18.2.0",
41
+ "ts-jest": "^29.1.1",
42
+ "ts-node": "^10.9.2",
43
+ "typescript": "^5.3.3"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/ribbon-studios/ribbon.git"
51
+ },
52
+ "engines": {
53
+ "browsers": "last 2 years, > 1%, not dead"
54
+ }
55
+ }
package/shell.nix ADDED
@@ -0,0 +1,16 @@
1
+ # Shell for bootstrapping flake-enabled nix and home-manager
2
+ # Enter it through 'nix develop' or (legacy) 'nix-shell'
3
+
4
+ { pkgs ? (import ./nixpkgs.nix) { } }: {
5
+ default = pkgs.mkShell {
6
+ # Enable experimental features without having to specify the argument
7
+ NIX_CONFIG = "experimental-features = nix-command flakes";
8
+ buildInputs = with pkgs; [
9
+ gnumake
10
+ nixpkgs-fmt
11
+ nixd
12
+ bun
13
+ nodejs_20
14
+ ];
15
+ };
16
+ }
@@ -0,0 +1,49 @@
1
+ .ribbon {
2
+ position: fixed;
3
+ z-index: 9999;
4
+ width: 80px;
5
+ height: 80px;
6
+ pointer-events: none;
7
+ user-select: none;
8
+ opacity: 1;
9
+ transform: rotate(45deg);
10
+ transition: opacity 0.15s ease-in-out;
11
+ backface-visibility: hidden;
12
+ }
13
+
14
+ .ribbon:hover {
15
+ opacity: 0.75;
16
+ }
17
+
18
+ .content {
19
+ font: bold 15px Sans-Serif;
20
+ text-align: center;
21
+ position: relative;
22
+ padding: 7px 0;
23
+ width: 150px;
24
+ box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.3);
25
+ pointer-events: all;
26
+ text-transform: uppercase;
27
+ transform: translate(-50%, -50%);
28
+ left: 50%;
29
+ top: 50%;
30
+ background-color: #fafafa;
31
+ color: #333;
32
+ }
33
+
34
+ .ribbon.bottom.right,
35
+ .ribbon.top.left {
36
+ transform: rotate(-45deg);
37
+ }
38
+ .ribbon.bottom {
39
+ bottom: -10px;
40
+ }
41
+ .ribbon.top {
42
+ top: -10px;
43
+ }
44
+ .ribbon.left {
45
+ left: -10px;
46
+ }
47
+ .ribbon.right {
48
+ right: -10px;
49
+ }
package/src/Ribbon.tsx ADDED
@@ -0,0 +1,51 @@
1
+ // eslint-disable-next-line @typescript-eslint/triple-slash-reference
2
+ /// <reference path="../global.d.ts" />
3
+
4
+ import { ComponentProps, ReactNode } from 'react';
5
+ import * as styles from './Ribbon.module.scss';
6
+
7
+ export type RibbonProps = {
8
+ /**
9
+ * The contents of the ribbon
10
+ */
11
+ children?: ReactNode;
12
+
13
+ /**
14
+ * The font color
15
+ *
16
+ * @default 'black'
17
+ */
18
+ color?: string;
19
+
20
+ /**
21
+ * The background color
22
+ *
23
+ * @default 'white'
24
+ */
25
+ backgroundColor?: string;
26
+
27
+ /**
28
+ * Whether the ribbon should be aligned at the 'top' or 'bottom'
29
+ *
30
+ * @default 'top-right'
31
+ */
32
+ position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
33
+ } & Pick<ComponentProps<'div'>, 'onClick'>;
34
+
35
+ export function Ribbon({
36
+ children,
37
+ backgroundColor = 'white',
38
+ color = 'black',
39
+ position = 'top-right',
40
+ ...props
41
+ }: RibbonProps) {
42
+ const [vertical, horizontal] = position.split('-');
43
+
44
+ return (
45
+ <div {...props} className={`${styles.ribbon} ${styles[vertical]} ${styles[horizontal]}`} data-testid="ribbon">
46
+ <div className={styles.content} style={{ backgroundColor, color }} data-testid="ribbon-content">
47
+ {children}
48
+ </div>
49
+ </div>
50
+ );
51
+ }
@@ -0,0 +1,75 @@
1
+ // eslint-disable-next-line @typescript-eslint/triple-slash-reference
2
+ /// <reference path="../../global.d.ts" />
3
+
4
+ import { render } from '@testing-library/react';
5
+ import { Chance } from 'chance';
6
+ import { Ribbon, RibbonProps } from '../';
7
+ import * as styles from '../Ribbon.module.scss';
8
+
9
+ const chance = new Chance();
10
+
11
+ describe('component(Ribbon)', () => {
12
+ it('should render the component', () => {
13
+ const component = render(<Ribbon />);
14
+
15
+ expect(component.baseElement).toBeTruthy();
16
+ });
17
+
18
+ describe('prop(children)', () => {
19
+ it('should support children', () => {
20
+ const expectedContent = chance.word();
21
+ const component = render(<Ribbon>{expectedContent}</Ribbon>);
22
+
23
+ expect(component.baseElement.innerHTML).toContain(expectedContent);
24
+ });
25
+ });
26
+
27
+ describe('prop(color)', () => {
28
+ it('should default to black', () => {
29
+ const component = render(<Ribbon />);
30
+
31
+ expect(component.getByTestId('ribbon-content').style.color).toEqual('black');
32
+ });
33
+
34
+ it('should support other colors', () => {
35
+ const component = render(<Ribbon color="rebeccapurple" />);
36
+
37
+ expect(component.getByTestId('ribbon-content').style.color).toEqual('rebeccapurple');
38
+ });
39
+ });
40
+
41
+ describe('prop(backgroundColor)', () => {
42
+ it('should default to white', () => {
43
+ const component = render(<Ribbon />);
44
+
45
+ expect(component.getByTestId('ribbon-content').style.backgroundColor).toEqual('white');
46
+ });
47
+
48
+ it('should support other colors', () => {
49
+ const component = render(<Ribbon backgroundColor="rebeccapurple" />);
50
+
51
+ expect(component.getByTestId('ribbon-content').style.backgroundColor).toEqual('rebeccapurple');
52
+ });
53
+ });
54
+
55
+ describe('prop(position)', () => {
56
+ it('should default to top-right', () => {
57
+ const component = render(<Ribbon />);
58
+
59
+ expect(component.getByTestId('ribbon').classList).toContain(styles.top);
60
+ expect(component.getByTestId('ribbon').classList).toContain(styles.right);
61
+ });
62
+
63
+ const positions: NonNullable<RibbonProps['position']>[] = ['top-left', 'bottom-right', 'bottom-left'];
64
+ for (const position of positions) {
65
+ it(`should support '${position}'`, () => {
66
+ const [v, h] = position.split('-');
67
+
68
+ const component = render(<Ribbon position={position} />);
69
+
70
+ expect(component.getByTestId('ribbon').classList).toContain(styles[v]);
71
+ expect(component.getByTestId('ribbon').classList).toContain(styles[h]);
72
+ });
73
+ }
74
+ });
75
+ });
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './Ribbon';
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es5",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "noEmit": true,
10
+ "incremental": true,
11
+ "esModuleInterop": true,
12
+ "module": "esnext",
13
+ "moduleResolution": "Node",
14
+ "resolveJsonModule": true,
15
+ "isolatedModules": true,
16
+ "jsx": "react-jsx",
17
+ "experimentalDecorators": true
18
+ },
19
+ "include": ["**/*.ts", "**/*.tsx"],
20
+ "exclude": ["node_modules"]
21
+ }