Skip to content

Commit ef3166f

Browse files
Initial commit
0 parents  commit ef3166f

File tree

13 files changed

+11197
-0
lines changed

13 files changed

+11197
-0
lines changed

.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
HTTPS=true
2+
PORT=3050

.github/workflows/build.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Build & Release
2+
3+
permissions:
4+
contents: write
5+
6+
on:
7+
push:
8+
branches: [ main ]
9+
workflow_dispatch:
10+
pull_request:
11+
12+
jobs:
13+
build-release:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
- name: Use Node.js 18.x
18+
uses: actions/setup-node@v4
19+
with:
20+
node-version: 18.x
21+
- run: yarn
22+
- run: yarn build
23+
- name: Create tar from built plugin
24+
run: |
25+
BUILDFOLDER="build"
26+
MANIFEST=$(find ./$BUILDFOLDER -name plugin-manifest.json)
27+
[ -z $MANIFEST ] && cp plugin-manifest $BUILDFOLDER
28+
MANIFEST="$BUILDFOLDER/plugin-manifest.json"
29+
VERSION=$(jq '.version' $MANIFEST -r)
30+
echo "version=$VERSION"
31+
mkdir -p output
32+
tar -C $BUILDFOLDER -czf output/$VERSION.tar.gz .
33+
ls -la output
34+
echo "version=$VERSION" >> $GITHUB_ENV
35+
- name: Create artifact
36+
uses: actions/upload-artifact@v4
37+
with:
38+
name: plugin-package
39+
path: output
40+
- name: Release built plugin
41+
uses: svenstaro/upload-release-action@v2
42+
with:
43+
repo_token: ${{ secrets.GITHUB_TOKEN }}
44+
file: output/*
45+
tag: ${{ env.version }}
46+
overwrite: true
47+
file_glob: true
48+
if: github.ref == 'refs/heads/main'

.gitignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# production
12+
/build
13+
14+
# misc
15+
.DS_Store
16+
.env.local
17+
.env.development.local
18+
.env.test.local
19+
.env.production.local
20+
21+
npm-debug.log*
22+
yarn-debug.log*
23+
yarn-error.log*
24+
25+
/public/plugin-manifest.json

.vscode/settings.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"[css][scss][javascript][typescript][javascriptreact][typescriptreact][json][mdx][html]": {
3+
"editor.formatOnSave": true,
4+
"editor.defaultFormatter": "esbenp.prettier-vscode"
5+
},
6+
}

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Example Flotiq Plugin in React
2+
3+
## Quickstart:
4+
5+
1. `yarn`
6+
2. `yarn start`
7+
3. work work work
8+
4. update your `src/plugin-manifest.json` file to contain the production URL and other plugin information
9+
5. `yarn build`
10+
6. paste js code from `./build/static/js/main.xxxxxxxx.js` to Flotiq console
11+
7. navigate to affected Flotiq pages
12+
13+
14+
## Deployment
15+
16+
<!-- TO DO -->
17+
18+
## Loading the plugin
19+
20+
### URL
21+
22+
1. Open Flotiq editor
23+
2. Open Chrome Dev console
24+
3. Execute the following script
25+
```javascript
26+
FlotiqPlugins.loadPlugin('plugin-id', '<URL TO COMPILED JS>')
27+
```
28+
4. Navigate to the view that is modified by the plugin
29+
30+
### Directly
31+
32+
1. Open Flotiq editor
33+
2. Open Chrome Dev console
34+
3. Paste the content of `static/js/main.xxxxxxxx.js`
35+
4. Navigate to the view that is modified by the plugin
36+
37+
### Deployment
38+
39+
1. Open Flotiq editor
40+
2. Add a new plugin and paste the URL to the hosted `plugin-manifest.json` file (you can use `https://localhost:3050/plugin-manifest.json` as long as you have accepted self-signed certificate for this url)
41+
3. Navigate to the view that is modified by the plugin

package.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"name": "flotiq-plugin-unejected",
3+
"version": "0.1.0",
4+
"private": true,
5+
"dependencies": {
6+
"@testing-library/jest-dom": "^5.17.0",
7+
"@testing-library/react": "^13.4.0",
8+
"@testing-library/user-event": "^13.5.0",
9+
"react": "^18.2.0",
10+
"react-dom": "^18.2.0",
11+
"react-scripts": "5.0.1",
12+
"web-vitals": "^2.1.4"
13+
},
14+
"scripts": {
15+
"start": "concurrently \"yarn start:manifest-watch\" \"yarn react-scripts start\"",
16+
"start:manifest-watch": "cpx -v ./src/plugin-manifest.json ./public/ --watch",
17+
"build": "cpx -v ./src/plugin-manifest.json ./public/ && react-scripts build",
18+
"test": "react-scripts test",
19+
"eject": "react-scripts eject"
20+
},
21+
"eslintConfig": {
22+
"extends": [
23+
"react-app",
24+
"react-app/jest"
25+
]
26+
},
27+
"browserslist": {
28+
"production": [
29+
">0.2%",
30+
"not dead",
31+
"not op_mini all"
32+
],
33+
"development": [
34+
"last 1 chrome version",
35+
"last 1 firefox version",
36+
"last 1 safari version"
37+
]
38+
},
39+
"devDependencies": {
40+
"concurrently": "^8.2.2",
41+
"cpx": "^1.5.0"
42+
}
43+
}

public/index.html

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<!--
2+
This file is only used for developing mode
3+
-->
4+
<!DOCTYPE html>
5+
<html lang="en">
6+
<head>
7+
<meta charset="utf-8" />
8+
<meta name="viewport" content="width=device-width, initial-scale=1" />
9+
<meta name="theme-color" content="#000000" />
10+
<meta
11+
name="description"
12+
content="Web site created using create-react-app"
13+
/>
14+
<title>Plugin preview</title>
15+
</head>
16+
<body>
17+
<noscript>You need to enable JavaScript to run this app.</noscript>
18+
<div id="plugin-preview-root"></div>
19+
</body>
20+
</html>

src/ShinyRow.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { useCallback, useMemo, useState } from "react";
2+
3+
function ShinyRow({ data }) {
4+
const [toggle, setToggle] = useState(false);
5+
const click = useCallback(() => {
6+
setToggle((t) => !t);
7+
}, []);
8+
9+
const style = useMemo(
10+
() => ({
11+
color: "rgb(0, 131, 252)",
12+
cursor: "pointer",
13+
fontStyle: toggle ? "italic" : "normal",
14+
fontWeight: toggle ? "bold" : "normal",
15+
}),
16+
[toggle]
17+
);
18+
19+
return (
20+
<button
21+
onClick={click}
22+
style={style}
23+
dangerouslySetInnerHTML={{ __html: data }}
24+
/>
25+
);
26+
}
27+
28+
export default ShinyRow;

src/index.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React from "react";
2+
import ReactDOM from "react-dom/client";
3+
import ShinyRow from "./ShinyRow";
4+
import {
5+
addElementToCache,
6+
getCachedElement,
7+
registerFn,
8+
} from "./plugin-helpers";
9+
import pluginInfo from "./plugin-manifest.json";
10+
11+
const updateApp = (root, data) => {
12+
root.render(<ShinyRow data={data} />);
13+
};
14+
15+
const initApp = (div, data) => {
16+
const root = ReactDOM.createRoot(div);
17+
updateApp(root, data);
18+
return root;
19+
};
20+
21+
registerFn(pluginInfo, (handler) => {
22+
handler.on(
23+
"flotiq.grid.cell::render",
24+
({ data, accessor, contentTypeName, contentObject }) => {
25+
if (accessor !== "title") return null;
26+
const key = `${contentTypeName}-${contentObject.id}-${accessor}`;
27+
const cachedApp = getCachedElement(key);
28+
if (cachedApp) {
29+
updateApp(cachedApp.root, data);
30+
return cachedApp.element;
31+
}
32+
33+
const div = document.createElement("div");
34+
addElementToCache(div, initApp(div, data), key);
35+
return div;
36+
}
37+
);
38+
});
39+
40+
const puginPreviewRoot = document.getElementById("plugin-preview-root");
41+
42+
if (puginPreviewRoot) initApp(puginPreviewRoot, "Hello World!");

src/plugin-helpers.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const appRoots = {};
2+
3+
export const addElementToCache = (element, root, key) => {
4+
appRoots[key] = {
5+
element,
6+
root,
7+
};
8+
9+
element.addEventListener("flotiq.detached", () => {
10+
setTimeout(() => {
11+
return delete appRoots[key];
12+
}, 50);
13+
});
14+
};
15+
16+
export const getCachedElement = (key) => {
17+
return appRoots[key];
18+
};
19+
20+
export const registerFn = (pluginInfo, callback) => {
21+
if (window.FlotiqPlugins?.add) {
22+
window.FlotiqPlugins.add(pluginInfo, callback);
23+
return;
24+
}
25+
if (!window.initFlotiqPlugins) window.initFlotiqPlugins = [];
26+
window.initFlotiqPlugins.push({ pluginInfo, callback });
27+
};

0 commit comments

Comments
 (0)