js-draw 0.0.4 → 0.0.5

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/CHANGELOG.md CHANGED
@@ -1,4 +1,10 @@
1
1
 
2
+ # 0.0.5
3
+ * Configuration options:
4
+ - Ability to disable touch panning
5
+ - The `new Editor(container, ...)` constructor now takes a configuration object as its second argument.
6
+ * A pre-bundled version of `js-draw` is now distributed.
7
+
2
8
  # 0.0.4
3
9
  * Preset shapes
4
10
  * Arrow
package/README.md CHANGED
@@ -23,6 +23,18 @@ const editor = new Editor(document.body);
23
23
  The `import js-draw/styles` step requires a bundler that can import `.css` files. For example, [`webpack` with `css-loader`.](https://webpack.js.org/loaders/css-loader/)
24
24
 
25
25
 
26
+ If you're not using a bundler, consider using the pre-bundled editor:
27
+ ```html
28
+ <!-- Replace 0.0.5 with the latest version of js-draw -->
29
+ <script src="https://cdn.jsdelivr.net/npm/js-draw@0.0.5/dist/bundle.js"></script>
30
+ <script>
31
+ const editor = new jsdraw.Editor(document.body);
32
+ editor.addToolbar();
33
+ editor.getRootElement().style.height = '600px';
34
+ </script>
35
+ ```
36
+
37
+
26
38
  ## Adding a toolbar
27
39
 
28
40
  To create a toolbar with the default tools:
@@ -74,3 +86,86 @@ but exports to
74
86
 
75
87
  which **does** contain the `<circle/>` element.
76
88
 
89
+ ## Settings/configuration
90
+ ### Disabling touchpad panning
91
+
92
+ Touchpad/mousewheel pan gestures can conflict with gestures used to scroll the document. To turn off touchpad pan gestures (and scrolling the editor with the mousewheel),
93
+ ```ts
94
+ const editor = new Editor(document.body, {
95
+ wheelEventsEnabled: false,
96
+ });
97
+ ```
98
+
99
+ ### Localization
100
+
101
+ See [src/localization.ts](src/localization.ts) for a list of strings.
102
+
103
+ Some of the default strings in the editor might be overridden like this:
104
+ ```ts
105
+ const editor = new Editor(document.body, {
106
+ // Example partial Spanish localization
107
+ localization: {
108
+ // Not all translated strings need to be specified. If a string isn't given,
109
+ // the English (default) localization will be used
110
+
111
+ // Strings for the main editor interface
112
+ // (see src/localization.ts)
113
+ loading: (percentage: number) => `Cargando: ${percentage}%...`,
114
+ imageEditor: 'Editor de dibujos',
115
+
116
+ undoAnnouncement: (commandDescription: string) => `${commandDescription} fue deshecho`,
117
+ redoAnnouncement: (commandDescription: string) => `${commandDescription} fue rehecho`,
118
+
119
+ // Strings for the toolbar
120
+ // (see src/toolbar/localization.ts)
121
+ pen: 'Lapiz',
122
+ eraser: 'Borrador',
123
+ select: 'Selecciona',
124
+ touchDrawing: 'Dibuja con un dedo',
125
+ thicknessLabel: 'Tamaño: ',
126
+ colorLabel: 'Color: ',
127
+
128
+ ...
129
+ },
130
+ });
131
+ ```
132
+
133
+
134
+ ## Changing the editor's color theme
135
+
136
+ The editor's color theme is specified using CSS. Its default theme looks like this:
137
+ ```css
138
+ .imageEditorContainer {
139
+ /* Deafult colors for the editor -- light mode */
140
+
141
+ --primary-background-color: white;
142
+ --primary-background-color-transparent: rgba(255, 255, 255, 0.5);
143
+ --secondary-background-color: #faf;
144
+ --primary-foreground-color: black;
145
+ --secondary-foreground-color: black;
146
+ }
147
+
148
+ @media (prefers-color-scheme: dark) {
149
+ .imageEditorContainer {
150
+ /* Deafult colors for the editor -- dark mode */
151
+
152
+ --primary-background-color: #151515;
153
+ --primary-background-color-transparent: rgba(50, 50, 50, 0.5);
154
+ --secondary-background-color: #607;
155
+ --primary-foreground-color: white;
156
+ --secondary-foreground-color: white;
157
+ }
158
+ }
159
+ ```
160
+
161
+ To override it, use a more specific CSS selector to set the theme variables. For example,
162
+ ```css
163
+ body .imageEditorContainer {
164
+ --primary-background-color: green;
165
+ --primary-background-color-transparent: rgba(255, 240, 200, 0.5);
166
+ --secondary-background-color: yellow;
167
+ --primary-foreground-color: black;
168
+ --secondary-foreground-color: black;
169
+ }
170
+ ```
171
+ disables the dark theme and creates a theme that primarially uses yellow/green colors.
@@ -0,0 +1,167 @@
1
+ // This file is taken from Joplin: https://github.com/laurent22/joplin
2
+ // js-draw was originally created as a part of a pull request for joplin. This
3
+ // is part of the functionality from Joplin it requires.
4
+
5
+ import { dirname, extname, basename } from 'path';
6
+ import TerserPlugin from 'terser-webpack-plugin';
7
+
8
+ import webpack from 'webpack';
9
+
10
+ export default class BundledFile {
11
+ private readonly bundleBaseName: string;
12
+ private readonly rootFileDirectory: string;
13
+ private readonly outputDirectory: string;
14
+ private readonly outputFilename: string;
15
+
16
+ public constructor(
17
+ public readonly bundleName: string,
18
+ private readonly sourceFilePath: string,
19
+ outputFilepath?: string,
20
+ ) {
21
+ this.rootFileDirectory = dirname(sourceFilePath);
22
+ this.bundleBaseName = basename(sourceFilePath, extname(sourceFilePath));
23
+
24
+ if (outputFilepath) {
25
+ this.outputDirectory = dirname(outputFilepath);
26
+ this.outputFilename = basename(outputFilepath);
27
+ } else {
28
+ this.outputDirectory = this.rootFileDirectory;
29
+ this.outputFilename = `${this.bundleBaseName}.bundle.js`;
30
+ }
31
+ }
32
+
33
+ private getWebpackOptions(mode: 'production' | 'development'): webpack.Configuration {
34
+ const config: webpack.Configuration = {
35
+ mode,
36
+ entry: this.sourceFilePath,
37
+ output: {
38
+ path: this.outputDirectory,
39
+ filename: this.outputFilename,
40
+
41
+ library: {
42
+ type: 'window',
43
+ name: this.bundleName,
44
+ },
45
+ },
46
+ // See https://webpack.js.org/guides/typescript/
47
+ module: {
48
+ rules: [
49
+ {
50
+ // Include .tsx to include react components
51
+ test: /\.tsx?$/i,
52
+ use: 'ts-loader',
53
+ exclude: /node_modules/,
54
+ },
55
+ {
56
+ test: /\.css$/i,
57
+ use: ['style-loader', 'css-loader'],
58
+ },
59
+ ],
60
+ },
61
+ optimization: {
62
+ minimizer: [
63
+ // Don't create separate files for comments.
64
+ // See https://stackoverflow.com/a/65650316/17055750
65
+ new TerserPlugin({
66
+ extractComments: false,
67
+ }),
68
+ ],
69
+ },
70
+ // Increase the minimum size required
71
+ // to trigger warnings.
72
+ // See https://stackoverflow.com/a/53517149/17055750
73
+ performance: {
74
+ maxAssetSize: 2_000_000, // 2-ish MiB
75
+ maxEntrypointSize: 2_000_000,
76
+ },
77
+ resolve: {
78
+ extensions: ['.tsx', '.ts', '.js'],
79
+ },
80
+ };
81
+
82
+ return config;
83
+ }
84
+
85
+ private handleErrors(err: Error | undefined | null, stats: webpack.Stats | undefined): boolean {
86
+ let failed = false;
87
+
88
+ if (err) {
89
+ console.error(`Error: ${err.name}`, err.message, err.stack);
90
+ failed = true;
91
+ } else if (stats?.hasErrors() || stats?.hasWarnings()) {
92
+ const data = stats.toJson();
93
+
94
+ if (data.warnings && data.warningsCount) {
95
+ console.warn('Warnings: ', data.warningsCount);
96
+ for (const warning of data.warnings) {
97
+ // Stack contains the message
98
+ if (warning.stack) {
99
+ console.warn(warning.stack);
100
+ } else {
101
+ console.warn(warning.message);
102
+ }
103
+ }
104
+ }
105
+ if (data.errors && data.errorsCount) {
106
+ console.error('Errors: ', data.errorsCount);
107
+ for (const error of data.errors) {
108
+ if (error.stack) {
109
+ console.error(error.stack);
110
+ } else {
111
+ console.error(error.message);
112
+ }
113
+ console.error();
114
+ }
115
+
116
+ failed = true;
117
+ }
118
+ }
119
+
120
+ return failed;
121
+ }
122
+
123
+ // Create a minified JS file in the same directory as `this.sourceFilePath` with
124
+ // the same name.
125
+ public build() {
126
+ const compiler = webpack(this.getWebpackOptions('production'));
127
+ return new Promise<void>((resolve, reject) => {
128
+ console.info(`Building bundle: ${this.bundleName}...`);
129
+
130
+ compiler.run((err, stats) => {
131
+ let failed = this.handleErrors(err, stats);
132
+
133
+ // Clean up.
134
+ compiler.close(async (error) => {
135
+ if (error) {
136
+ console.error('Error cleaning up:', error);
137
+ failed = true;
138
+ }
139
+ if (!failed) {
140
+ console.log('☑ Done building! ☑');
141
+ resolve();
142
+ } else {
143
+ reject();
144
+ }
145
+ });
146
+ });
147
+ });
148
+ }
149
+
150
+ public startWatching() {
151
+ const compiler = webpack(this.getWebpackOptions('development'));
152
+ const watchOptions = {
153
+ ignored: [
154
+ '**/node_modules',
155
+ '**/dist',
156
+ ],
157
+ };
158
+
159
+ console.info('Watching bundle: ', this.bundleName);
160
+ compiler.watch(watchOptions, async (err, stats) => {
161
+ const failed = this.handleErrors(err, stats);
162
+ if (!failed) {
163
+ console.log('☑ Built! ☑');
164
+ }
165
+ });
166
+ }
167
+ }
@@ -0,0 +1,11 @@
1
+ import { dirname } from 'path';
2
+ import BundledFile from './BundledFile';
3
+
4
+ const rootDir = dirname(__dirname);
5
+ const mainBundle = new BundledFile(
6
+ 'jsdraw',
7
+ `${rootDir}/src/bundle/bundled.ts`,
8
+ `${rootDir}/dist/bundle.js`,
9
+ );
10
+
11
+ void mainBundle.build();
@@ -0,0 +1,13 @@
1
+ export default class BundledFile {
2
+ readonly bundleName: string;
3
+ private readonly sourceFilePath;
4
+ private readonly bundleBaseName;
5
+ private readonly rootFileDirectory;
6
+ private readonly outputDirectory;
7
+ private readonly outputFilename;
8
+ constructor(bundleName: string, sourceFilePath: string, outputFilepath?: string);
9
+ private getWebpackOptions;
10
+ private handleErrors;
11
+ build(): Promise<void>;
12
+ startWatching(): void;
13
+ }
@@ -0,0 +1,157 @@
1
+ // This file is taken from Joplin: https://github.com/laurent22/joplin
2
+ // js-draw was originally created as a part of a pull request for joplin. This
3
+ // is part of the functionality from Joplin it requires.
4
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
5
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
6
+ return new (P || (P = Promise))(function (resolve, reject) {
7
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
8
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
9
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
10
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
11
+ });
12
+ };
13
+ import { dirname, extname, basename } from 'path';
14
+ import TerserPlugin from 'terser-webpack-plugin';
15
+ import webpack from 'webpack';
16
+ export default class BundledFile {
17
+ constructor(bundleName, sourceFilePath, outputFilepath) {
18
+ this.bundleName = bundleName;
19
+ this.sourceFilePath = sourceFilePath;
20
+ this.rootFileDirectory = dirname(sourceFilePath);
21
+ this.bundleBaseName = basename(sourceFilePath, extname(sourceFilePath));
22
+ if (outputFilepath) {
23
+ this.outputDirectory = dirname(outputFilepath);
24
+ this.outputFilename = basename(outputFilepath);
25
+ }
26
+ else {
27
+ this.outputDirectory = this.rootFileDirectory;
28
+ this.outputFilename = `${this.bundleBaseName}.bundle.js`;
29
+ }
30
+ }
31
+ getWebpackOptions(mode) {
32
+ const config = {
33
+ mode,
34
+ entry: this.sourceFilePath,
35
+ output: {
36
+ path: this.outputDirectory,
37
+ filename: this.outputFilename,
38
+ library: {
39
+ type: 'window',
40
+ name: this.bundleName,
41
+ },
42
+ },
43
+ // See https://webpack.js.org/guides/typescript/
44
+ module: {
45
+ rules: [
46
+ {
47
+ // Include .tsx to include react components
48
+ test: /\.tsx?$/i,
49
+ use: 'ts-loader',
50
+ exclude: /node_modules/,
51
+ },
52
+ {
53
+ test: /\.css$/i,
54
+ use: ['style-loader', 'css-loader'],
55
+ },
56
+ ],
57
+ },
58
+ optimization: {
59
+ minimizer: [
60
+ // Don't create separate files for comments.
61
+ // See https://stackoverflow.com/a/65650316/17055750
62
+ new TerserPlugin({
63
+ extractComments: false,
64
+ }),
65
+ ],
66
+ },
67
+ // Increase the minimum size required
68
+ // to trigger warnings.
69
+ // See https://stackoverflow.com/a/53517149/17055750
70
+ performance: {
71
+ maxAssetSize: 2000000,
72
+ maxEntrypointSize: 2000000,
73
+ },
74
+ resolve: {
75
+ extensions: ['.tsx', '.ts', '.js'],
76
+ },
77
+ };
78
+ return config;
79
+ }
80
+ handleErrors(err, stats) {
81
+ let failed = false;
82
+ if (err) {
83
+ console.error(`Error: ${err.name}`, err.message, err.stack);
84
+ failed = true;
85
+ }
86
+ else if ((stats === null || stats === void 0 ? void 0 : stats.hasErrors()) || (stats === null || stats === void 0 ? void 0 : stats.hasWarnings())) {
87
+ const data = stats.toJson();
88
+ if (data.warnings && data.warningsCount) {
89
+ console.warn('Warnings: ', data.warningsCount);
90
+ for (const warning of data.warnings) {
91
+ // Stack contains the message
92
+ if (warning.stack) {
93
+ console.warn(warning.stack);
94
+ }
95
+ else {
96
+ console.warn(warning.message);
97
+ }
98
+ }
99
+ }
100
+ if (data.errors && data.errorsCount) {
101
+ console.error('Errors: ', data.errorsCount);
102
+ for (const error of data.errors) {
103
+ if (error.stack) {
104
+ console.error(error.stack);
105
+ }
106
+ else {
107
+ console.error(error.message);
108
+ }
109
+ console.error();
110
+ }
111
+ failed = true;
112
+ }
113
+ }
114
+ return failed;
115
+ }
116
+ // Create a minified JS file in the same directory as `this.sourceFilePath` with
117
+ // the same name.
118
+ build() {
119
+ const compiler = webpack(this.getWebpackOptions('production'));
120
+ return new Promise((resolve, reject) => {
121
+ console.info(`Building bundle: ${this.bundleName}...`);
122
+ compiler.run((err, stats) => {
123
+ let failed = this.handleErrors(err, stats);
124
+ // Clean up.
125
+ compiler.close((error) => __awaiter(this, void 0, void 0, function* () {
126
+ if (error) {
127
+ console.error('Error cleaning up:', error);
128
+ failed = true;
129
+ }
130
+ if (!failed) {
131
+ console.log('☑ Done building! ☑');
132
+ resolve();
133
+ }
134
+ else {
135
+ reject();
136
+ }
137
+ }));
138
+ });
139
+ });
140
+ }
141
+ startWatching() {
142
+ const compiler = webpack(this.getWebpackOptions('development'));
143
+ const watchOptions = {
144
+ ignored: [
145
+ '**/node_modules',
146
+ '**/dist',
147
+ ],
148
+ };
149
+ console.info('Watching bundle: ', this.bundleName);
150
+ compiler.watch(watchOptions, (err, stats) => __awaiter(this, void 0, void 0, function* () {
151
+ const failed = this.handleErrors(err, stats);
152
+ if (!failed) {
153
+ console.log('☑ Built! ☑');
154
+ }
155
+ }));
156
+ }
157
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { dirname } from "path";
2
+ import BundledFile from "./BundledFile";
3
+ const rootDir = dirname(__dirname);
4
+ const mainBundle = new BundledFile('jsdraw', `${rootDir}/src/bundle/bundled.ts`, `${rootDir}/dist/bundle.js`);
5
+ void mainBundle.build();