@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.
- package/.codeclimate.yml +3 -0
- package/.editorconfig +13 -0
- package/.eslintrc +15 -0
- package/.gitattributes +0 -0
- package/.github/workflows/ci.yml +104 -0
- package/.nvmrc +1 -0
- package/.prettierrc +7 -0
- package/.releaserc.yml +2 -0
- package/.vscode/settings.json +24 -0
- package/CONTRIBUTING.md +22 -0
- package/README.md +54 -0
- package/__tests__/identity-obj-proxy-esm.ts +13 -0
- package/bun.lockb +0 -0
- package/demo/App.tsx +44 -0
- package/demo/index.html +19 -0
- package/demo/index.tsx +4 -0
- package/dist/index.css +55 -0
- package/dist/index.css.map +1 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -0
- package/dist/module.js +58 -0
- package/dist/module.js.map +1 -0
- package/dist/types.d.ts +28 -0
- package/dist/types.d.ts.map +1 -0
- package/flake.lock +27 -0
- package/flake.nix +15 -0
- package/global.d.ts +5 -0
- package/jest.config.ts +30 -0
- package/nixpkgs.nix +8 -0
- package/package.json +55 -0
- package/shell.nix +16 -0
- package/src/Ribbon.module.scss +49 -0
- package/src/Ribbon.tsx +51 -0
- package/src/__tests__/index.spec.tsx +75 -0
- package/src/index.ts +1 -0
- package/tsconfig.json +21 -0
package/.codeclimate.yml
ADDED
package/.editorconfig
ADDED
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
package/.releaserc.yml
ADDED
|
@@ -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
|
+
}
|
package/CONTRIBUTING.md
ADDED
|
@@ -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
|
+

|
|
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
|
+
}
|
package/demo/index.html
ADDED
|
@@ -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
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"}
|
package/dist/types.d.ts
ADDED
|
@@ -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
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
|
+
}
|