@vonaffenfels/contentful-teasermanager 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/.babelrc ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "presets": [
3
+ "next/babel",
4
+ [
5
+ "@babel/preset-env",
6
+ {
7
+ "modules": false,
8
+ "targets": {
9
+ "browsers": [
10
+ "last 2 Chrome versions",
11
+ "last 2 Firefox versions",
12
+ "last 2 Safari versions",
13
+ "last 2 iOS versions",
14
+ "last 1 Android version",
15
+ "last 1 ChromeAndroid version",
16
+ "ie 11"
17
+ ]
18
+ }
19
+ }
20
+ ],
21
+ "@babel/preset-react"
22
+ ],
23
+ "plugins": [
24
+ [
25
+ "@babel/plugin-proposal-private-property-in-object",
26
+ {
27
+ "loose": true
28
+ }
29
+ ],
30
+ [
31
+ "@babel/plugin-proposal-class-properties",
32
+ {
33
+ "loose": true
34
+ }
35
+ ],
36
+ "@babel/plugin-syntax-dynamic-import"
37
+ ]
38
+ }
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ v14.16.1
package/package.json ADDED
@@ -0,0 +1,105 @@
1
+ {
2
+ "name": "@vonaffenfels/contentful-teasermanager",
3
+ "version": "1.0.4",
4
+ "scripts": {
5
+ "prepublish": "yarn run build",
6
+ "dev": "yarn run start",
7
+ "test": "echo \"Error: no test specified\" && exit 1",
8
+ "build": "echo \"Don't build this module!\"",
9
+ "link": "yarn link",
10
+ "start": "webpack serve --config webpack.config.dev.js"
11
+ },
12
+ "peerDependencies": {
13
+ "@babel/core": "^7.20.12",
14
+ "@babel/plugin-proposal-class-properties": "^7.13.0",
15
+ "@babel/plugin-syntax-dynamic-import": "^7.8.3",
16
+ "@babel/preset-env": "^7.13.15",
17
+ "@babel/preset-react": "^7.13.13",
18
+ "@contentful/app-sdk": "^3.33.0",
19
+ "@contentful/field-editor-single-line": "^0.14.1",
20
+ "@contentful/field-editor-test-utils": "^0.11.1",
21
+ "@contentful/forma-36-fcss": "^0.3.1",
22
+ "@contentful/forma-36-react-components": "^3.88.3",
23
+ "@contentful/forma-36-tokens": "^0.10.1",
24
+ "@svgr/webpack": "^5.5.0",
25
+ "babel-loader": "^8.2.2",
26
+ "classnames": "^2.3.2",
27
+ "clean-webpack-plugin": "^3.0.0",
28
+ "cloudinary": "^1.25.1",
29
+ "contentful-management": "^7.17.0",
30
+ "copy-webpack-plugin": "^8.1.1",
31
+ "cross-env": "^7.0.3",
32
+ "css-loader": "^5.2.4",
33
+ "cssnano": "^5.0.1",
34
+ "date-fns": "^2.30.0",
35
+ "dotenv-webpack": "^7.0.2",
36
+ "file-loader": "^6.2.0",
37
+ "html-webpack-plugin": "^5.3.1",
38
+ "mini-css-extract-plugin": "^1.5.0",
39
+ "postcss": "8.4.14",
40
+ "postcss-loader": "^5.2.0",
41
+ "raw-loader": "^4.0.2",
42
+ "react": "18.2.0",
43
+ "react-dom": "18.2.0",
44
+ "react-uid": "^2.3.1",
45
+ "regenerator-runtime": "^0.14.0",
46
+ "sass": "^1.32.11",
47
+ "sass-loader": "^11.0.1",
48
+ "speakingurl": "^14.0.1",
49
+ "style-loader": "^2.0.0",
50
+ "tailwindcss": "3.3.2",
51
+ "url-loader": "^4.1.1",
52
+ "webpack-cli": "^4.6.0",
53
+ "webpack-dev-server": "^4.0.0-beta.2"
54
+ },
55
+ "devDependencies": {
56
+ "@babel/core": "^7.20.12",
57
+ "@babel/plugin-proposal-class-properties": "^7.13.0",
58
+ "@babel/plugin-syntax-dynamic-import": "^7.8.3",
59
+ "@babel/preset-env": "^7.13.15",
60
+ "@babel/preset-react": "^7.13.13",
61
+ "@contentful/app-sdk": "^3.33.0",
62
+ "@contentful/field-editor-single-line": "^0.14.1",
63
+ "@contentful/field-editor-test-utils": "^0.11.1",
64
+ "@contentful/forma-36-fcss": "^0.3.1",
65
+ "@contentful/forma-36-react-components": "^3.88.3",
66
+ "@contentful/forma-36-tokens": "^0.10.1",
67
+ "@svgr/webpack": "^5.5.0",
68
+ "babel-loader": "^8.2.2",
69
+ "classnames": "^2.3.2",
70
+ "clean-webpack-plugin": "^3.0.0",
71
+ "cloudinary": "^1.25.1",
72
+ "contentful-management": "^7.17.0",
73
+ "copy-webpack-plugin": "^8.1.1",
74
+ "cross-env": "^7.0.3",
75
+ "css-loader": "^5.2.4",
76
+ "cssnano": "^5.0.1",
77
+ "date-fns": "^2.30.0",
78
+ "dotenv-webpack": "^7.0.2",
79
+ "file-loader": "^6.2.0",
80
+ "html-webpack-plugin": "^5.3.1",
81
+ "mini-css-extract-plugin": "^1.5.0",
82
+ "postcss": "8.4.14",
83
+ "postcss-loader": "^5.2.0",
84
+ "raw-loader": "^4.0.2",
85
+ "react": "18.2.0",
86
+ "react-dom": "18.2.0",
87
+ "react-uid": "^2.3.1",
88
+ "regenerator-runtime": "^0.14.0",
89
+ "sass": "^1.32.11",
90
+ "sass-loader": "^11.0.1",
91
+ "speakingurl": "^14.0.1",
92
+ "style-loader": "^2.0.0",
93
+ "tailwindcss": "3.3.2",
94
+ "url-loader": "^4.1.1",
95
+ "webpack-cli": "^4.6.0",
96
+ "webpack-dev-server": "^4.0.0-beta.2"
97
+ },
98
+ "dependencies": {
99
+ "webpack": "5.73.0"
100
+ },
101
+ "gitHead": "ba6d3fea2cca04a822de7e3d7dff5949e62a9d46",
102
+ "publishConfig": {
103
+ "access": "public"
104
+ }
105
+ }
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ plugins: {
3
+ 'postcss-import': {},
4
+ tailwindcss: {},
5
+ autoprefixer: {},
6
+ },
7
+ };
@@ -0,0 +1,71 @@
1
+ import React, {Component} from 'react';
2
+ import {
3
+ Heading, Form, TextField, Workbench, Paragraph,
4
+ } from '@contentful/forma-36-react-components';
5
+ import {css} from 'emotion';
6
+
7
+ export default class Config extends Component {
8
+
9
+ constructor(props) {
10
+ super(props);
11
+ this.state = {parameters: {}};
12
+
13
+ // `onConfigure` allows to configure a callback to be
14
+ // invoked when a user attempts to install the app or update
15
+ // its configuration.
16
+ props.sdk.app.onConfigure(() => this.onConfigure());
17
+ }
18
+
19
+ async componentDidMount() {
20
+ // Get current parameters of the app.
21
+ // If the app is not installed yet, `parameters` will be `null`.
22
+ const parameters = await this.props.sdk.app.getParameters();
23
+
24
+ this.setState(parameters ? {parameters} : this.state, () => {
25
+ // Once preparation has finished, call `setReady` to hide
26
+ // the loading screen and present the app to a user.
27
+ this.props.sdk.app.setReady();
28
+ });
29
+ }
30
+
31
+ async onConfigure() {
32
+ // This method will be called when a user clicks on "Install"
33
+ // or "Save" in the configuration screen.
34
+ // for more details see https://www.contentful.com/developers/docs/extensibility/ui-extensions/sdk-reference/#register-an-app-configuration-hook
35
+
36
+ // Get current the state of EditorInterface and other entities
37
+ // related to this app installation
38
+ const currentState = await this.props.sdk.app.getCurrentState();
39
+
40
+ return {
41
+ // Parameters to be persisted as the app configuration.
42
+ parameters: this.state.parameters,
43
+ // In case you don't want to submit any update to app
44
+ // locations, you can just pass the currentState as is
45
+ targetState: currentState,
46
+ };
47
+ }
48
+
49
+ onFieldChanged(value, field) {
50
+ this.setState({
51
+ parameters: {
52
+ ...this.state.parameters,
53
+ [field]: value,
54
+ },
55
+ });
56
+ }
57
+
58
+ render() {
59
+ return (
60
+ <Workbench className={css({
61
+ padding: '80px',
62
+ backgroundColor: "#FFF",
63
+ })}>
64
+ <Form>
65
+ <Heading>No Configuration necessary</Heading>
66
+ </Form>
67
+ </Workbench>
68
+ );
69
+ }
70
+
71
+ }
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+
3
+ const Dialog = ({sdk}) => {
4
+ return <div>DIALOG</div>
5
+ };
6
+
7
+ export default Dialog;
@@ -0,0 +1,45 @@
1
+ import React, {
2
+ useEffect, useState,
3
+ } from 'react';
4
+ import {Teasermanager} from "../Teasermanager";
5
+
6
+ const Entry = ({sdk}) => {
7
+ const contentField = sdk.parameters.instance.contentField;
8
+ const [locale, setLocale] = useState(sdk.locales.default);
9
+ const [portal, setPortal] = useState();
10
+ const portalField = sdk?.entry?.fields?.portal;
11
+
12
+ portalField?.onValueChanged(() => {
13
+ if (portalField.getValue() !== portal) {
14
+ setPortal(portalField.getValue());
15
+ }
16
+ });
17
+
18
+ useEffect(() => {
19
+ const detachHandler = sdk.editor.onLocaleSettingsChanged((loc) => {
20
+ setLocale(loc?.focused || sdk.locales.default);
21
+ });
22
+
23
+ return () => {
24
+ detachHandler();
25
+ };
26
+ }, []);
27
+
28
+ const onSlotClick = (slotId, currentDate) => {
29
+ console.log("KIENZ_DEBUG: :31 / onSlotClick", {
30
+ slotId,
31
+ currentDate,
32
+ });
33
+ sdk.dialogs.selectSingleEntry({contentTypes: ["article"]}).then((entry) => {
34
+ if (!entry) {
35
+ return;
36
+ }
37
+
38
+ console.log(`Selected entry ${entry.sys.id} for slot ${slotId} and date ${currentDate}`);
39
+ });
40
+ };
41
+
42
+ return <Teasermanager entryId={sdk.entry.getSys().id} locale={locale} contentFieldName={contentField} onSlotClick={onSlotClick}/>;
43
+ };
44
+
45
+ export default Entry;
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import {Teasermanager} from "../Teasermanager";
3
+
4
+ const Page = ({sdk}) => {
5
+
6
+ const onSlotClick = (slotId, currentDate) => {
7
+ console.log("KIENZ_DEBUG: :7 / onSlotClick", {sdk});
8
+ }
9
+
10
+ return <Teasermanager entryId={"4RvZ6fcUtBIgw2Hw5YILPs"} onSlotClick={onSlotClick} />;
11
+ };
12
+
13
+ export default Page;
@@ -0,0 +1,83 @@
1
+ import {
2
+ useEffect, useState,
3
+ } from "react";
4
+ import styles from "./Timeline.module.css";
5
+ import format from "date-fns/format";
6
+ import subtract from "date-fns/sub";
7
+ import classNames from "classnames";
8
+
9
+ export const Timeline = ({
10
+ currentDate,
11
+ setCurrentDate,
12
+ }) => {
13
+ const stepsInRange = (24 * 60) / 15 + 1; // must be uneven so we have a center ;)
14
+
15
+ useEffect(() => {
16
+ const currentDate = new Date();
17
+ currentDate.setMinutes((Math.round(currentDate.getMinutes() / 15) * 15) % 60);
18
+
19
+ setCurrentDate(currentDate);
20
+ }, []);
21
+
22
+ const leftDate = currentDate && subtract(currentDate, {minutes: stepsInRange / 2 * 15});
23
+ if (leftDate) {
24
+ leftDate.setMinutes((Math.round(currentDate.getMinutes() / 15) * 15) % 60);
25
+ }
26
+
27
+ const handleDateChange = (e) => {
28
+ if (!e.target.value) {
29
+ return;
30
+ }
31
+
32
+ let newDate = new Date(e.target.value);
33
+
34
+ newDate.setHours(currentDate.getHours(), currentDate.getMinutes());
35
+
36
+ setCurrentDate(newDate);
37
+ };
38
+
39
+ const handleTimeChange = e => {
40
+ if (!e.target.value) {
41
+ return;
42
+ }
43
+
44
+ let newDate = new Date(currentDate);
45
+
46
+ let [hours, minutes] = e.target.value.split(":");
47
+
48
+ newDate.setHours(hours, minutes);
49
+
50
+ setCurrentDate(newDate);
51
+ };
52
+
53
+ return <div className="flex w-full">
54
+ <div className={styles.wrapper}>
55
+ <div className={styles.date}>
56
+ {!!currentDate && (
57
+ <>
58
+ <input type="date" value={format(currentDate, "yyyy-MM-dd")} onChange={handleDateChange} className="mr-4" />
59
+ <input type="time" value={format(currentDate, "HH:mm")} onChange={handleTimeChange} />
60
+ </>
61
+ )}
62
+ </div>
63
+ <div className={styles.timeline}>
64
+ {!!leftDate && Array(stepsInRange).fill(0).map((_, index) => {
65
+ const dotDate = new Date(leftDate.getTime() + (15 * 60 * 1000 * (index + 1)));
66
+
67
+ return <div
68
+ key={index}
69
+ className={classNames(styles.timelineDot, {[styles.timelineDotActive]: dotDate.getTime() === currentDate.getTime()})}
70
+ onClick={e => {
71
+ e.preventDefault();
72
+ setCurrentDate(dotDate);
73
+ return false;
74
+ }}>
75
+ <div className={classNames(styles.timelineDotLabel, {[styles.timelineDotLabelPermanent]: dotDate.getHours() % 6 === 0 && dotDate.getMinutes() === 0})}>
76
+ {leftDate && format(dotDate, "dd.MM.yyyy HH:mm")}
77
+ </div>
78
+ </div>;
79
+ })}
80
+ </div>
81
+ </div>
82
+ </div>;
83
+ };
@@ -0,0 +1,58 @@
1
+ .wrapper {
2
+ display: flex;
3
+ flex-direction: column;
4
+ position: relative;
5
+ width: 100%;
6
+ overflow: hidden;
7
+ }
8
+
9
+ .timeline {
10
+ background-color: #e1fff9;
11
+ height: auto;
12
+ padding: 6px 0 6px 0;
13
+ width: 100%;
14
+ display: flex;
15
+ font-weight: bold;
16
+ font-size: 1em;
17
+ justify-content: space-between;
18
+ }
19
+
20
+ .timeline-dot {
21
+ display: block;
22
+ width: 10px;
23
+ height: 10px;
24
+ background-color: #004f73;
25
+ border-radius: 9999px;
26
+ cursor: pointer;
27
+ position: relative;
28
+ }
29
+
30
+ .timeline-dot:hover .timeline-dot-label {
31
+ display: block;
32
+ z-index: 100;
33
+ }
34
+
35
+ .timeline-dot-active {
36
+ outline: 2px solid red;
37
+ }
38
+
39
+ .timeline-dot-label.timeline-dot-label-permanent {
40
+ display: block;
41
+ z-index: 10;
42
+ }
43
+
44
+ .timeline-dot-label {
45
+ display: none;
46
+ background: white;
47
+ position: absolute;
48
+ top: -20px;
49
+ left: -100px;
50
+ width: 200px;
51
+ text-align: center;
52
+ }
53
+
54
+ .date {
55
+ width: 100%;
56
+ text-align: center;
57
+ padding: 1em 0 1em 0;
58
+ }
@@ -0,0 +1,81 @@
1
+ import React, {useEffect, useRef, useState} from "react";
2
+
3
+ import {getContentfulClient} from "../lib/contentfulClient";
4
+ import Renderer from "@vonaffenfels/slate-editor/dist/Renderer";
5
+ import componentLoader from "@vonaffenfels/slate-editor/componentLoader";
6
+ import {Timeline} from "./Teasermanager/Timeline";
7
+ import styles from "./Teasermanager.module.css";
8
+
9
+ export const Teasermanager = ({entryId, onSlotClick = () => console.error("missing onSlotClick"), contentFieldName = "wysiwyg", locale = "en-US"}) => {
10
+ const contentfulClient = getContentfulClient();
11
+ const [entry, setEntry] = useState(null);
12
+ const wrapperRef = useRef(null);
13
+ const [currentDate, setCurrentDate] = useState(null);
14
+
15
+ const initSlot = (node) => {
16
+ if (node.dataset.__teasermanager_slot_initialized) {
17
+ return;
18
+ }
19
+
20
+ node.dataset.__teasermanager_slot_initialized = true;
21
+
22
+ const managementNode = document.createElement("div");
23
+ managementNode.classList.add(styles.management);
24
+
25
+ const managementLabelNode = document.createElement("div");
26
+ managementLabelNode.classList.add(styles.label);
27
+ managementLabelNode.innerHTML = `Klicken um den Artikel zu wechseln`;
28
+
29
+ managementNode.appendChild(managementLabelNode);
30
+
31
+ managementNode.addEventListener("click", (e) => {
32
+ const slotId = node.dataset.teasermanagerSlot;
33
+ onSlotClick(slotId, currentDate);
34
+ });
35
+
36
+ node.appendChild(managementNode);
37
+ }
38
+
39
+ const updateSlots = (mutationList) => {
40
+ const slots = wrapperRef.current.querySelectorAll(`*[data-teasermanager-slot]`);
41
+ slots.forEach(initSlot);
42
+ }
43
+
44
+ useEffect(() => {
45
+ if (!entryId && entry) {
46
+ setEntry(null);
47
+ return;
48
+ }
49
+
50
+ contentfulClient.getEntry(entryId).then((loadedEntry, b) => {
51
+ setEntry(loadedEntry);
52
+ });
53
+ }, []);
54
+
55
+ useEffect(() => {
56
+ if (!wrapperRef.current) {
57
+ return;
58
+ }
59
+
60
+ const observer = new MutationObserver(updateSlots);
61
+ observer.observe(wrapperRef.current, {attributes: false, childList: true, subtree: true});
62
+
63
+ return () => {
64
+ observer.disconnect();
65
+ }
66
+ }, [wrapperRef, currentDate]);
67
+
68
+ return <div className="w-full flex flex-col">
69
+ <div className="w-full">
70
+ <Timeline currentDate={currentDate} setCurrentDate={setCurrentDate} />
71
+ </div>
72
+ <div className={styles.wrapper} ref={wrapperRef}>
73
+ {!!entry?.fields?.[contentFieldName]?.[locale] &&
74
+ <Renderer
75
+ value={entry?.fields?.[contentFieldName]?.[locale]}
76
+ storybookComponentLoader={componentLoader}/>}
77
+ </div>
78
+
79
+ </div>
80
+
81
+ }
@@ -0,0 +1,38 @@
1
+ .wrapper *[data-teasermanager-slot] {
2
+ position: relative;
3
+ }
4
+
5
+ .management {
6
+ font-family: Arial, Helvetica, sans-serif;
7
+
8
+ background-color: rgba(255, 255, 255, 0.4);
9
+ border: 1px dashed black;
10
+ color: black;
11
+ text-align: center;
12
+ vertical-align: middle;
13
+ position: absolute;
14
+ top: 0;
15
+ left: 0;
16
+ width: 100%;
17
+ height: 100%;
18
+ cursor: pointer;
19
+
20
+ display: flex;
21
+ justify-items: center;
22
+ align-items: center;
23
+ padding: 2em;
24
+ }
25
+
26
+ .management:hover {
27
+ background-color: rgba(255, 255, 255, 0.9);
28
+ }
29
+
30
+ .label {
31
+ visibility: hidden;
32
+ font-weight: bold;
33
+ }
34
+
35
+ .management:hover .label {
36
+ visibility: visible;
37
+ }
38
+
package/src/dev.js ADDED
@@ -0,0 +1,4 @@
1
+ import './scss/dev.scss';
2
+ import {BaseContentfulApp} from "./index"
3
+
4
+ BaseContentfulApp();
@@ -0,0 +1,24 @@
1
+ import React, {
2
+ useState, useEffect, useRef,
3
+ } from "react";
4
+
5
+ export default function useOnScreen(ref) {
6
+ const [isIntersecting, setIntersecting] = useState(false);
7
+ const observerRef = useRef();
8
+
9
+ useEffect(() => {
10
+ if (!observerRef.current && ref.current) {
11
+ observerRef.current = new IntersectionObserver(([entry]) => setIntersecting(entry.isIntersecting));
12
+
13
+ observerRef.current.observe(ref.current);
14
+
15
+ return () => {
16
+ if (observerRef.current) {
17
+ observerRef.current.disconnect();
18
+ }
19
+ };
20
+ }
21
+ }, []);
22
+
23
+ return isIntersecting;
24
+ }
package/src/index.html ADDED
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8"/>
5
+ <title>Slate Block Editor Contentful App</title>
6
+ <script src="https://media-library.cloudinary.com/global/all.js"></script>
7
+ </head>
8
+ <body>
9
+ <div id="root">
10
+ </div>
11
+ </body>
12
+ </html>
package/src/index.js ADDED
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import {createRoot} from 'react-dom/client';
3
+
4
+ import {
5
+ init,
6
+ locations,
7
+ } from '@contentful/app-sdk';
8
+ import '@contentful/forma-36-react-components/dist/styles.css';
9
+ import '@contentful/forma-36-fcss/dist/styles.css';
10
+ import '@contentful/forma-36-tokens/dist/css/index.css';
11
+ import '@vonaffenfels/slate-editor/dist/index.css';
12
+ import './scss/index.scss';
13
+
14
+ import EntryEditor from './components/Contentful/EntryEditor';
15
+ import Config from './components/Contentful/ConfigScreen';
16
+ import Dialog from "./components/Contentful/Dialog";
17
+ import Page from "./components/Contentful/Page";
18
+ import {initContentfulClient} from "./lib/contentfulClient";
19
+
20
+ export const BaseContentfulApp = () => {
21
+ init((sdk) => {
22
+ const rootContainer = document.getElementById('root');
23
+
24
+ initContentfulClient(sdk);
25
+
26
+ const ComponentLocationSettings = [
27
+ {
28
+ location: locations.LOCATION_APP_CONFIG,
29
+ component: <Config sdk={sdk}/>,
30
+ },
31
+ {
32
+ location: locations.LOCATION_ENTRY_EDITOR,
33
+ component: <EntryEditor sdk={sdk}/>,
34
+ },
35
+ {
36
+ location: locations.LOCATION_DIALOG,
37
+ component: <Dialog sdk={sdk}/>,
38
+ },
39
+ {
40
+ location: locations.LOCATION_PAGE,
41
+ component: <Page sdk={sdk}/>,
42
+ },
43
+ ];
44
+
45
+ ComponentLocationSettings.forEach((componentLocationSetting) => {
46
+ if (sdk.location.is(componentLocationSetting.location)) {
47
+ const root = createRoot(rootContainer); // createRoot(container!) if you use TypeScript
48
+ root.render(componentLocationSetting.component);
49
+ }
50
+ });
51
+ });
52
+ };
@@ -0,0 +1,14 @@
1
+ let __contentfulClient;
2
+
3
+ export const getContentfulClient = () => {
4
+ if (!__contentfulClient) {
5
+ throw new Error(`Contentful client not initialized run initContentfulClient(sdk) first`);
6
+ }
7
+
8
+ return __contentfulClient;
9
+ }
10
+
11
+ export const initContentfulClient = (sdk) => {
12
+ __contentfulClient = sdk.space;
13
+ }
14
+
File without changes
@@ -0,0 +1,12 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ html, body, div {
6
+ margin: 0;
7
+ padding: 0;
8
+ border: 0;
9
+ font-size: 100%;
10
+ vertical-align: baseline;
11
+ font: inherit;
12
+ }
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ content: [
3
+ './src/**/*.js',
4
+ '../../@base/slate-editor/src/dev/testComponents/**/*.js',
5
+ ],
6
+ };
@@ -0,0 +1,26 @@
1
+ const path = require('path');
2
+ let config = require("./webpack.config")({
3
+ componentImportRoot: "@vonaffenfels/slate-editor/src/dev/testComponents",
4
+ relativePathToComponents: path.resolve(__dirname + "../../../@base/slate-editor/src/dev/testComponents"),
5
+ });
6
+
7
+
8
+ config.entry = {index: ["regenerator-runtime/runtime.js", './src/dev.js']};
9
+
10
+ config.mode = "development";
11
+ config.watch = true;
12
+ config.watchOptions = {ignored: ['dist/**', 'node_modules/**']};
13
+ config.cache = {type: "memory"};
14
+
15
+ config.output.publicPath = "/dist/";
16
+ config.devtool = 'inline-source-map';
17
+
18
+ config.devServer = {
19
+ static: path.join(__dirname, 'dist'),
20
+ compress: true,
21
+ historyApiFallback: true,
22
+ port: process.env.PORT || 5842,
23
+ devMiddleware: {writeToDisk: true},
24
+ };
25
+
26
+ module.exports = config;
@@ -0,0 +1,146 @@
1
+ const path = require('path');
2
+ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
3
+ const HtmlWebpackPlugin = require('html-webpack-plugin');
4
+ const webpack = require("webpack");
5
+ const {readFileSync} = require("fs");
6
+ const {CleanWebpackPlugin} = require('clean-webpack-plugin');
7
+
8
+ module.exports = ({
9
+ babelConfig = JSON.parse(readFileSync("../../.babelrc").toString()),
10
+ componentImportRoot = "@vonaffenfels/slate-editor/src/dev/testComponents",
11
+ relativePathToComponents = path.resolve(__dirname + "../../../@base/slate-editor/src/dev/testComponents"),
12
+ postCssConfig = {
13
+ plugins: {
14
+ 'postcss-import': {},
15
+ tailwindcss: {},
16
+ autoprefixer: {},
17
+ },
18
+ },
19
+ transpilePaths = [],
20
+ }) => {
21
+ return {
22
+ mode: 'production',
23
+ entry: {index: ["regenerator-runtime/runtime.js", './src/index.js']},
24
+ module: {
25
+ rules: [
26
+ {
27
+ test: /componentLoader.js/i,
28
+ use: [
29
+ {
30
+ loader: "@vonaffenfels/slate-editor/componentLoader.js",
31
+ options: {
32
+ componentRoot: relativePathToComponents,
33
+ componentImportRoot: componentImportRoot,
34
+ },
35
+ },
36
+ {
37
+ loader: 'babel-loader',
38
+ options: babelConfig,
39
+ },
40
+ ],
41
+ },
42
+ {
43
+ test: /\.js$/,
44
+ include: [
45
+ path.resolve(__dirname, 'src'),
46
+ relativePathToComponents,
47
+ ...transpilePaths,
48
+ ],
49
+ use: [
50
+ {
51
+ loader: 'babel-loader',
52
+ options: babelConfig,
53
+ },
54
+ ],
55
+ },
56
+ {
57
+ test: /\.s[ac]ss$/i,
58
+ include: path.resolve(__dirname, 'src', 'scss'),
59
+ use: [
60
+ "style-loader",
61
+ {loader: 'css-loader', options: {importLoaders: 1}},
62
+ {loader: 'postcss-loader', options: {postcssOptions: postCssConfig}},
63
+ "sass-loader",
64
+ ],
65
+ },
66
+ {
67
+ test: /module\.css$/i,
68
+ use: [
69
+ 'style-loader',
70
+ {
71
+ loader: 'css-loader',
72
+ options: {
73
+ importLoaders: 1,
74
+ modules: {exportLocalsConvention: "camelCase"},
75
+ },
76
+ },
77
+ {loader: 'postcss-loader', options: {postcssOptions: postCssConfig}},
78
+ ],
79
+ },
80
+ {
81
+ test: /\.css$/i,
82
+ exclude: [/module\.css$/i],
83
+ use: [
84
+ 'style-loader',
85
+ {
86
+ loader: 'css-loader',
87
+ options: {importLoaders: 1},
88
+ },
89
+ {loader: 'postcss-loader', options: {postcssOptions: postCssConfig}},
90
+ ],
91
+ },
92
+ {
93
+ test: /\.svg$/,
94
+ resourceQuery: {not: [/url/]}, // exclude react component if *.svg?url
95
+ use: [
96
+ {
97
+ loader: '@svgr/webpack',
98
+ options: {
99
+ dimensions: false,
100
+ svgo: false,
101
+ },
102
+ },
103
+ 'url-loader',
104
+ ],
105
+ },
106
+ {
107
+ test: /\.(woff(2)?|ttf|eot|png)(\?v=\d+\.\d+\.\d+)?$/,
108
+ use: [
109
+ {
110
+ loader: 'file-loader',
111
+ options: {
112
+ name: '[name].[ext]',
113
+ outputPath: 'fonts/',
114
+ },
115
+ },
116
+ ],
117
+ },
118
+ {
119
+ test: /\.(graphql|gql)$/,
120
+ loader: 'raw-loader',
121
+ },
122
+ ],
123
+ },
124
+ plugins: [
125
+ new CleanWebpackPlugin(),
126
+ new HtmlWebpackPlugin({template: path.join(__dirname, 'src', 'index.html')}),
127
+ new MiniCssExtractPlugin({}),
128
+ new webpack.DefinePlugin({'process.env': JSON.stringify(process.env)}),
129
+ ],
130
+ resolve: {
131
+ extensions: ['.json', '.js', '.jsx'],
132
+ fallback: {
133
+ stream: false,
134
+ path: false,
135
+ timers: false,
136
+ },
137
+ },
138
+ output: {
139
+ filename: '[name].js',
140
+ libraryTarget: "umd",
141
+ publicPath: "/",
142
+ globalObject: "this",
143
+ path: path.resolve(__dirname, 'dist'),
144
+ },
145
+ };
146
+ };