├── .editorconfig
├── .gitignore
├── .husky
├── .gitignore
└── pre-commit
├── .prettierrc
├── eslint.config.mjs
├── license.txt
├── locales
├── index.js
├── locales
│ ├── cn.js
│ ├── de.js
│ └── en.js
└── package.json
├── package.json
├── readme.md
├── site
├── index.html
├── index.js
├── package.json
├── public
│ └── placeholder.md
├── src
│ ├── Demo.svelte
│ ├── Main.svelte
│ ├── ThemeSelect.svelte
│ └── data.js
├── svelte.config.js
└── vite.config.js
├── svelte
├── .gitignore
├── cypress.config.js
├── cypress
│ ├── e2e
│ │ └── demos.cy.js
│ ├── fixtures
│ │ └── example.json
│ └── support
│ │ ├── commands.js
│ │ └── e2e.js
├── demos
│ ├── cases
│ │ ├── ActionMenu.svelte
│ │ ├── BackendComplex.svelte
│ │ ├── BasicInit.svelte
│ │ ├── Batches.svelte
│ │ ├── ChangesAuto.svelte
│ │ ├── ChangesConfirmed.svelte
│ │ ├── Comments.svelte
│ │ ├── CustomButtons.svelte
│ │ ├── Gantt.svelte
│ │ ├── Kanban.svelte
│ │ ├── Layouts.svelte
│ │ ├── Locales.svelte
│ │ ├── ModalColumns.svelte
│ │ ├── ModalPanel.svelte
│ │ ├── MultipleCombos.svelte
│ │ ├── NotImplemented.svelte
│ │ ├── Overflow.svelte
│ │ ├── RequiredFields.svelte
│ │ ├── SaveValidatedValues.svelte
│ │ ├── Scheduler.svelte
│ │ ├── SidebarPanel.svelte
│ │ ├── SubPanels.svelte
│ │ ├── Tasklist.svelte
│ │ ├── ToolbarValues.svelte
│ │ └── Validation.svelte
│ ├── common
│ │ ├── Index.svelte
│ │ ├── Link.svelte
│ │ ├── ListRoutes.svelte
│ │ ├── Router.svelte
│ │ └── helpers.js
│ ├── custom
│ │ ├── gantt
│ │ │ └── Links.svelte
│ │ ├── kanban
│ │ │ ├── PriorityCombo.svelte
│ │ │ ├── UserIcon.svelte
│ │ │ └── UserMultiselect.svelte
│ │ └── scheduler
│ │ │ ├── ColorPickerSchema.svelte
│ │ │ ├── Combo.svelte
│ │ │ ├── DateTimePicker.svelte
│ │ │ ├── Multiselect.svelte
│ │ │ ├── Uploader.svelte
│ │ │ └── templates
│ │ │ ├── ComboOption.svelte
│ │ │ └── MultiselectOption.svelte
│ ├── data.js
│ ├── index.js
│ └── routes.js
├── index.html
├── license.txt
├── package.json
├── postcss.config.js
├── readme.md
├── src
│ ├── components
│ │ ├── Columns.svelte
│ │ ├── Editor.svelte
│ │ ├── Form.svelte
│ │ ├── FormEditor.svelte
│ │ ├── Helpers.svelte.js
│ │ ├── Layout.svelte
│ │ ├── Values.svelte
│ │ ├── buttons
│ │ │ ├── Toolbar.svelte
│ │ │ └── buttons.js
│ │ └── sections
│ │ │ ├── ReadOnly.svelte
│ │ │ └── Section.svelte
│ ├── editor.js
│ ├── en.js
│ ├── helpers.js
│ ├── index.js
│ └── themes
│ │ ├── Material.svelte
│ │ ├── Willow.svelte
│ │ └── WillowDark.svelte
├── svelte.config.js
├── tests
│ ├── Index.svelte
│ ├── cases
│ │ └── LocalData.svelte
│ ├── data.js
│ ├── index.html
│ ├── index.js
│ └── routes.js
├── vite.config.js
└── whatsnew.md
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = tab
5 | indent_size = 4
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package-lock.json
3 |
4 | *.zip
5 | .Ds_store
6 | *.tgz
7 | *.log
8 | .vscode
9 | .idea
10 | .env.local
--------------------------------------------------------------------------------
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | yarn run lint-staged
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": true,
3 | "semi": true,
4 | "singleQuote": false,
5 | "quoteProps": "as-needed",
6 | "trailingComma": "es5",
7 | "bracketSpacing": true,
8 | "arrowParens": "avoid",
9 | "svelteSortOrder": "options-scripts-markup-styles",
10 | "plugins": [
11 | "prettier-plugin-svelte"
12 | ],
13 | "overrides": [
14 | {
15 | "files": "*.svelte",
16 | "options": {
17 | "parser": "svelte"
18 | }
19 | },
20 | {
21 | "files": "*.ts",
22 | "options": {
23 | "parser": "typescript"
24 | }
25 | }
26 | ]
27 | }
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import eslintConfigPrettier from "eslint-config-prettier";
2 | import eslintPluginSvelte from 'eslint-plugin-svelte';
3 | import * as svelteParser from 'svelte-eslint-parser';
4 | import tsLint from "typescript-eslint";
5 | import jsLint from "@eslint/js";
6 | import vitest from "eslint-plugin-vitest";
7 | import globals from "globals";
8 |
9 | export default [{
10 | ignores: ["node_modules/", "dist/", "build/", "coverage/", "public/", "svelte/vite.config.js"],
11 | },
12 | jsLint.configs.recommended,
13 | ...tsLint.configs.recommended,
14 | ...eslintPluginSvelte.configs['flat/recommended'],
15 | eslintConfigPrettier,
16 | vitest.configs.recommended,
17 | ...eslintPluginSvelte.configs["flat/prettier"],
18 | {
19 | rules: {
20 | "no-bitwise": ["error"],
21 | // there is a misconception between esLint and svelte compiler
22 | // rules that are necessary for compiler, throw errors in esLint
23 | // need to be revised with next version of toolchain
24 | "svelte/no-unused-svelte-ignore": "off",
25 | "svelte/valid-compile": "off",
26 | // Ignore unused vars starting with _
27 | // "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
28 | // // Turn off the need for explicit function return types
29 | // "@typescript-eslint/explicit-function-return-type": "off",
30 | // // Warn when "any" type is used
31 | "@typescript-eslint/no-explicit-any": "off",
32 | // // Warn on @ts-ignore comments
33 | // "@typescript-eslint/ban-ts-comment": "warn",
34 | // // Public methods should have return types
35 | // "@typescript-eslint/explicit-module-boundary-types": "error",
36 | },
37 | },
38 | {
39 | languageOptions: {
40 | globals: { ...globals.browser, ...globals.es2022 },
41 | ecmaVersion: 2022,
42 | sourceType: "module",
43 | parserOptions: {
44 | extraFileExtensions: [".svelte"],
45 | warnOnUnsupportedTypeScriptVersion: false,
46 | tsconfigRootDir: import.meta.dirname,
47 | },
48 | },
49 |
50 | },
51 | {
52 |
53 | files: ["**/*.svelte"],
54 | rules: {
55 | "@typescript-eslint/no-unused-expressions": "off"
56 | }
57 | },
58 | {
59 | // temporarily ignore cypress folder
60 | ignores: [
61 | "**/cypress/"
62 | ]
63 | },
64 | {
65 |
66 | files: ["**/*.svelte.js"],
67 | languageOptions: {
68 | parser: svelteParser,
69 | }
70 | }
71 | ];
72 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 XB Software Sp. z o.o.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/locales/index.js:
--------------------------------------------------------------------------------
1 | export { default as en } from "./locales/en";
2 | export { default as cn } from "./locales/cn";
3 | export { default as de } from "./locales/de";
4 |
--------------------------------------------------------------------------------
/locales/locales/cn.js:
--------------------------------------------------------------------------------
1 | export default {
2 | editor: {
3 | "This field is required": "该字段为必填字段",
4 | "Invalid value": "无效值",
5 | Yes: "是",
6 | No: "不",
7 | Save: "节省",
8 | Cancel: "取消",
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/locales/locales/de.js:
--------------------------------------------------------------------------------
1 | export default {
2 | editor: {
3 | "This field is required": "Dieses Feld ist erforderlich",
4 | "Invalid value": "Ungültiger Wert",
5 | Yes: "Ja",
6 | No: "Nein",
7 | Save: "Speichern",
8 | Cancel: "Abbrechen",
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/locales/locales/en.js:
--------------------------------------------------------------------------------
1 | export default {
2 | editor: {
3 | "This field is required": "This field is required",
4 | "Invalid value": "Invalid value",
5 | Yes: "Yes",
6 | No: "No",
7 | Save: "Save",
8 | Cancel: "Cancel",
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/locales/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wx-editor-locales",
3 | "version": "2.1.2",
4 | "description": "Locales for WX Editor widget",
5 | "type": "module",
6 | "main": "index.js",
7 | "scripts": {
8 | "build": "true",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "keywords": [
12 | "wx",
13 | "editor",
14 | "locales"
15 | ],
16 | "author": "",
17 | "license": "MIT"
18 | }
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "wx-editor",
4 | "workspaces": [
5 | "svelte",
6 | "locales",
7 | "site"
8 | ],
9 | "scripts": {
10 | "build:deps": "true",
11 | "build:site": "cd site && yarn build",
12 | "build:tests": "cd svelte && yarn build:tests",
13 | "build": "cd svelte && yarn build",
14 | "lint": "yarn eslint ./svelte/src ./svelte/demos ./locales",
15 | "prepare": "husky",
16 | "start:demos": "cd svelte && yarn start",
17 | "start:site": "cd site && yarn start",
18 | "start:tests": "cd svelte && yarn start:tests",
19 | "start": "cd svelte && yarn start",
20 | "test:cypress": "cd svelte && yarn test:cypress",
21 | "test": "true"
22 | },
23 | "devDependencies": {
24 | "@sveltejs/vite-plugin-svelte": "4.0.0",
25 | "@vitest/coverage-v8": "1.6.0",
26 | "wx-vite-tools": "1.0.5",
27 | "autoprefixer": "10.4.20",
28 | "cypress": "13.6.4",
29 | "eslint": "9.14.0",
30 | "eslint-config-prettier": "9.1.0",
31 | "eslint-plugin-cypress": "4.1.0",
32 | "eslint-plugin-svelte": "2.46.0",
33 | "eslint-plugin-vitest": "0.5.4",
34 | "husky": "9.1.6",
35 | "lint-staged": "15.2.10",
36 | "npm-run-all": "4.1.5",
37 | "postcss": "8.4.47",
38 | "prettier": "3.3.3",
39 | "prettier-plugin-svelte": "3.2.7",
40 | "rollup-plugin-visualizer": "5.12.0",
41 | "shx": "0.3.4",
42 | "svelte": "5.1.9",
43 | "svelte-spa-router": "4.0.1",
44 | "typescript-eslint": "8.13.0",
45 | "typescript": "5.6.3",
46 | "vite-plugin-conditional-compile": "1.4.5",
47 | "vite-plugin-dts": "3.7.2",
48 | "vite": "5.4.10",
49 | "vitest": "1.5.0"
50 | },
51 | "lint-staged": {
52 | "*.{ts,js,svelte}": [
53 | "eslint --fix --no-warn-ignored",
54 | "prettier --write"
55 | ],
56 | "*.{css,md,json}": [
57 | "prettier --write"
58 | ]
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # SVAR Svelte Editor
4 |
5 | [](https://www.npmjs.com/package/wx-svelte-editor)
6 | [](https://github.com/svar-widgets/editor/blob/main/license.txt)
7 | [](https://www.npmjs.com/package/wx-svelte-editor)
8 |
9 |
10 |
11 |
12 |
13 | [Documentation](https://docs.svar.dev/svelte/editor/) • [Demos](https://docs.svar.dev/svelte/editor/samples/#/base/willow)
14 |
15 |
16 |
17 | An intuitive Svelte component for creating content editing forms to manage data within UI elements on a page. You can use it for editing structured data like table rows, informational cards, blocks with text, etc.
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | ### :sparkles: Key features:
26 |
27 | - **Flexible display options**: add the editor as a modal popup, inline form or as a seamless sidebar for convenient access.
28 | - **Multiple input types**: Use various input fields like text inputs, checkboxes, date pickers, sliders, and more controls from [SVAR Core](https://github.com/svar-widgets/core) library.
29 | - **Built-in validation**: Includes basic validation for required fields and supports custom validation rules for advanced scenarios.
30 | - **Flexible save options**: Choose between manual saves, auto-save, or custom saving logic adjusted to your needs.
31 | - **Compact layout**: Organize forms into expandable sections or a 2-column layout for efficient use of screen space.
32 |
33 | ### :hammer_and_wrench: How to Use
34 |
35 | To use the Editor widget, simply import the package and include the component in your Svelte file:
36 |
37 | ```svelte
38 |
43 |
44 |
45 | ```
46 | For more details, visit the [getting started guide](https://docs.svar.dev/svelte/editor/getting_started/).
47 |
48 | ### :wrench: How to Modify
49 |
50 | Typically, you don't need to modify the code. However, if you wish to do so, follow these steps:
51 |
52 | 1. Run `yarn` to install dependencies. Note that this project is a monorepo using `yarn` workspaces, so npm will not work
53 | 2. Start the project in development mode with `yarn start`
54 |
55 | ### :white_check_mark: Run Tests
56 |
57 | To run the test:
58 |
59 | 1. Start the test examples with:
60 | ```sh
61 | yarn start:tests
62 | ```
63 | 2. In a separate console, run the end-to-end tests with:
64 | ```sh
65 | yarn test:cypress
66 | ```
67 |
--------------------------------------------------------------------------------
/site/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Svelte Widgets
7 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/site/index.js:
--------------------------------------------------------------------------------
1 | import Demo from "./src/Demo.svelte";
2 | import { mount } from "svelte";
3 |
4 | mount(Demo, {
5 | target: document.querySelector("#wx_demo_area") || document.body,
6 | props: {
7 | themeSelect: true,
8 | },
9 | });
10 |
--------------------------------------------------------------------------------
/site/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wx-site-svelte-editor",
3 | "version": "2.1.2",
4 | "type": "module",
5 | "scripts": {
6 | "build": "vite build",
7 | "lint": "yarn eslint ./src",
8 | "start": "yarn vite --open"
9 | },
10 | "license": "MIT",
11 | "dependencies": {
12 | "wx-svelte-editor": "2.1.2",
13 | "wx-svelte-core": "2.0.1"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/site/public/placeholder.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/svar-widgets/editor/14a818bba18addf54ce128482bde226bc1f93d95/site/public/placeholder.md
--------------------------------------------------------------------------------
/site/src/Demo.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | {#if themeSelect}
25 |
36 | {:else}
37 |
38 | {/if}
39 |
40 |
41 |
42 |
43 |
76 |
--------------------------------------------------------------------------------
/site/src/Main.svelte:
--------------------------------------------------------------------------------
1 |
35 |
36 |
37 | {#each data as card}
38 |
39 |
40 |
(selected = card)}
43 | class:active={selected?.id === card.id}
44 | >
45 |
49 |
50 |
{card.title}
51 |
62 |
{card.description || ""}
63 |
77 |
78 |
79 | {/each}
80 |
81 |
82 | {#if selected}
83 |
91 | {/if}
92 |
93 |
162 |
--------------------------------------------------------------------------------
/site/src/ThemeSelect.svelte:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 |
25 | {#snippet children(option)}
26 |
27 |
31 |
{option.label}
32 |
33 |
51 | {/snippet}
52 |
53 |
54 |
--------------------------------------------------------------------------------
/site/src/data.js:
--------------------------------------------------------------------------------
1 | export function getData() {
2 | const users = [
3 | {
4 | id: 1,
5 | label: "Alice Johnson",
6 | name: "Alice Johnson",
7 | color: "#FF5387",
8 | initials: "AJ",
9 | },
10 | {
11 | id: 2,
12 | label: "Carol Martinez",
13 | name: "Carol Martinez",
14 | color: "#00D19A",
15 | initials: "CM",
16 | },
17 | {
18 | id: 3,
19 | label: "Bob Smith",
20 | name: "Bob Smith",
21 | color: "#37A9EF",
22 | initials: "BS",
23 | },
24 | {
25 | id: 4,
26 | label: "David Lee",
27 | name: "David Lee",
28 | color: "#8A62DF",
29 | initials: "DL",
30 | },
31 | ];
32 |
33 | const priority = [
34 | { id: 1, label: "High", color: "#DF282F" },
35 | { id: 2, label: "Medium", color: "#FFC975" },
36 | { id: 3, label: "Low", color: "#65D3B3" },
37 | ];
38 |
39 | const items = [
40 | {
41 | key: "title",
42 | comp: "text",
43 | label: "Title",
44 | column: "left",
45 | required: true,
46 | },
47 | {
48 | key: "description",
49 | comp: "textarea",
50 | label: "Description",
51 | column: "left",
52 | required: true,
53 | },
54 | {
55 | comp: "select",
56 | label: "Priority",
57 | key: "priority",
58 | options: priority,
59 | column: "left",
60 | },
61 | {
62 | comp: "slider",
63 | key: "progress",
64 | column: "left",
65 | labelTemplate: value => `Progress ${value}%`,
66 | },
67 | {
68 | comp: "date",
69 | key: "start_date",
70 | label: "Due date",
71 | column: "left",
72 | required: true,
73 | },
74 | {
75 | comp: "select",
76 | key: "assigned",
77 | label: "Assigned to",
78 | options: users,
79 | column: "left",
80 | },
81 | {
82 | comp: "comments",
83 | key: "comments",
84 | label: "Comments",
85 | users,
86 | activeUser: 1,
87 | },
88 | ];
89 |
90 | const data = [
91 | {
92 | id: 1,
93 | title: "List of New Features",
94 | priority: 2,
95 | description:
96 | "Develop detailed wireframes for the upcoming features of the web app. This includes sketching out the user interface for new functionalities, ensuring they are consistent with the existing design language, and preparing for user feedback sessions. The wireframes should be clear and detailed, providing a solid foundation for the subsequent design and development phases.",
97 | start_date: new Date(2024, 7, 5),
98 | progress: 20,
99 | assigned: 1,
100 | comments: [
101 | {
102 | id: 1,
103 | user: 1,
104 | cardId: 1,
105 | content:
106 | "I've started sketching the initial wireframes and will share the drafts by the end of the week.",
107 | date: new Date(),
108 | },
109 | {
110 | id: 2,
111 | user: 2,
112 | cardId: 1,
113 | content:
114 | "Great, Alice! Can you ensure the new dashboard aligns with our design?",
115 | date: new Date(),
116 | },
117 | {
118 | id: 3,
119 | user: 1,
120 | cardId: 1,
121 | content:
122 | "Absolutely, I’m cross-referencing the design guide as we speak.",
123 | date: new Date(),
124 | },
125 | // {
126 | // id: 4,
127 | // user: 3,
128 | // cardId: 1,
129 | // content:"Make sure to leave some space for potential additional widgets. We might need to add more features later.",
130 | // date: new Date(),
131 | // },
132 | ],
133 | },
134 | {
135 | id: 2,
136 | title: "Implement User Authentication",
137 | priority: 1,
138 | description:
139 | "Develop and integrate a secure user authentication system using OAuth 2.0. The system should support user registration, login, password recovery, and account management. Ensure it includes robust security measures to protect user data, handles various authentication scenarios, and provides a seamless user experience.",
140 | start_date: new Date(2024, 7, 10),
141 | progress: 85,
142 | assigned: 3,
143 | comments: [
144 | {
145 | id: 5,
146 | user: 3,
147 | cardId: 2,
148 | content:
149 | "I'll start by setting up the OAuth 2.0 framework and then integrate the registration and login functionality.",
150 | date: new Date(),
151 | },
152 | {
153 | id: 6,
154 | user: 1,
155 | cardId: 2,
156 | content:
157 | "Do you need any UI components for the login and registration forms?",
158 | date: new Date(),
159 | },
160 | {
161 | id: 7,
162 | user: 3,
163 | cardId: 2,
164 | content:
165 | "Yes, it would be great to have a consistent design for those.",
166 | date: new Date(),
167 | },
168 | // {
169 | // id:8,
170 | // user:4,
171 | // cardId: 2,
172 | // content:"For password recovery, we should consider both email and SMS options. What do you think?",
173 | // date:new Date()
174 | // },
175 | // {
176 | // id:9,
177 | // user:3,
178 | // cardId: 2,
179 | // content:"Good idea, David. I’ll plan for both. Let’s discuss the details in our next meeting.",
180 | // date:new Date()
181 | // }
182 | ],
183 | },
184 | {
185 | id: 3,
186 | title: "Conduct Usability Testing",
187 | priority: 3,
188 | description:
189 | "Plan and execute usability testing sessions with target users to identify usability issues and gather feedback. Prepare testing scripts, recruit participants, and facilitate testing sessions. Analyze the results to uncover insights into user behavior and pain points. Provide actionable recommendations to improve the user experience based on the collected data.",
190 | start_date: new Date(2024, 7, 8),
191 | progress: 30,
192 | assigned: 2,
193 | comments: [
194 | {
195 | id: 10,
196 | user: 3,
197 | cardId: 3,
198 | content:
199 | "I've drafted the testing scripts. Please review and provide feedback.",
200 | date: new Date(),
201 | },
202 | {
203 | id: 11,
204 | user: 1,
205 | cardId: 3,
206 | content:
207 | "I’ll take a look today. Any specific areas we should focus on?",
208 | date: new Date(),
209 | },
210 | {
211 | id: 12,
212 | user: 3,
213 | cardId: 3,
214 | content:
215 | "Mainly navigation flow and form usability. We need to ensure a smooth user experience.",
216 | date: new Date(),
217 | },
218 | // {
219 | // id: 13,
220 | // user: 2,
221 | // cardId: 3,
222 | // content: "I've recruited participants for the testing. Sessions will be conducted over the next three days.",
223 | // date: new Date(),
224 | // },
225 | // {
226 | // id: 14,
227 | // user: 4,
228 | // cardId: 3,
229 | // content: "After the testing, let’s meet to discuss the findings and prioritize the issues.",
230 | // date: new Date(),
231 | // },
232 | ],
233 | },
234 | {
235 | id: 4,
236 | title: "Optimize Database Queries",
237 | priority: 2,
238 | description:
239 | "Review and optimize database queries to enhance performance. This involves identifying and analyzing slow-performing queries, adding appropriate indexing, and refactoring inefficient queries. The goal is to reduce load times and improve data retrieval efficiency. Document changes and ensure that optimizations do not compromise data integrity or application functionality.",
240 | start_date: new Date(2024, 7, 12),
241 | progress: 15,
242 | assigned: 4,
243 | comments: [
244 | {
245 | id: 15,
246 | user: 4,
247 | cardId: 4,
248 | content:
249 | "I've identified a few slow queries that need immediate attention.",
250 | date: new Date(),
251 | },
252 | {
253 | id: 16,
254 | user: 1,
255 | cardId: 2,
256 | content:
257 | "David, can you share the list of queries you’re focusing on? I might have some insights.",
258 | date: new Date(),
259 | },
260 | {
261 | id: 17,
262 | user: 4,
263 | cardId: 4,
264 | content:
265 | "Sure, Bob. I’ll upload the list to our shared folder.",
266 | date: new Date(),
267 | },
268 | // {
269 | // id: 18,
270 | // user: 1,
271 | // cardId: 4,
272 | // content: "Once you’re done, can you also look at the queries related to the reporting module? We’ve had some delays there.",
273 | // date: new Date(),
274 | // },
275 | // {
276 | // id: 19,
277 | // user: 4,
278 | // cardId: 4,
279 | // content: "I’ll include those in my next round of optimizations. Thanks for the heads-up, Alice.",
280 | // date: new Date(),
281 | // },
282 | ],
283 | },
284 | ];
285 |
286 | return { items, data, users, priority };
287 | }
288 |
--------------------------------------------------------------------------------
/site/svelte.config.js:
--------------------------------------------------------------------------------
1 | import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
2 |
3 | export default {
4 | // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
5 | // for more information about preprocessors
6 | preprocess: vitePreprocess(),
7 | };
8 |
--------------------------------------------------------------------------------
/site/vite.config.js:
--------------------------------------------------------------------------------
1 | import { resolve } from "path";
2 | import { svelte } from "@sveltejs/vite-plugin-svelte";
3 |
4 | export default () => {
5 | let build,
6 | publicDir = resolve(__dirname, "public"),
7 | server = {},
8 | base = "",
9 | plugins = [svelte({})];
10 |
11 | build = {
12 | rollupOptions: {
13 | input: { index: resolve(__dirname, "index.html") },
14 | },
15 | };
16 |
17 | return {
18 | base,
19 | build,
20 | publicDir,
21 | resolve: { dedupe: ["svelte"] },
22 | plugins,
23 | server,
24 | };
25 | };
26 |
--------------------------------------------------------------------------------
/svelte/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | cypress/screenshots
3 | cypress/videos
4 |
--------------------------------------------------------------------------------
/svelte/cypress.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "cypress";
2 |
3 | export default defineConfig({
4 | video: false,
5 | e2e: {
6 | setupNodeEvents() {},
7 | },
8 | });
9 |
--------------------------------------------------------------------------------
/svelte/cypress/e2e/demos.cy.js:
--------------------------------------------------------------------------------
1 | const cases = [
2 | "/base/:skin",
3 | "/base/:skin",
4 | "/modal/:skin",
5 | "/columns/:skin",
6 | "/sidebar/:skin",
7 | "/changes-auto/:skin",
8 | "/changes-confirmed/:skin",
9 | "/subpanels/:skin",
10 | "/batches/:skin",
11 | "/actions/:skin",
12 | "/buttons/:skin",
13 | "/comments/:skin",
14 | "/taslkist/:skin",
15 | ];
16 |
17 | const skins = ["material", "willow", "willow-dark"];
18 | const links = [];
19 |
20 | cases.forEach(w => {
21 | skins.forEach(s => {
22 | links.push(w.replace(":skin", s));
23 | });
24 | });
25 |
26 | context("Basic functionality", () => {
27 | it("widget", () => {
28 | links.forEach(w => {
29 | cy.visit(`/index.html#${w}`);
30 | cy.shot(w, { area: ".content" });
31 | });
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/svelte/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
6 |
--------------------------------------------------------------------------------
/svelte/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | Cypress.Commands.add("shot", (...args) => {
2 | // eslint-disable-next-line cypress/no-unnecessary-waiting
3 | cy.wait(100);
4 |
5 | const name = args.filter(a => typeof a !== "object").join("-");
6 | const conf =
7 | typeof args[args.length - 1] === "object" ? args[args.length - 1] : {};
8 | const sconf = { ...conf, overwrite: true };
9 |
10 | if (conf.area) cy.get(conf.area).screenshot(name, sconf);
11 | else cy.screenshot(name, sconf);
12 | });
13 |
--------------------------------------------------------------------------------
/svelte/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import "./commands";
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/svelte/demos/cases/ActionMenu.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 | {message}
15 |
16 |
17 |
Top Bar, Right
18 |
19 |
55 |
56 |
57 |
58 |
59 |
86 |
--------------------------------------------------------------------------------
/svelte/demos/cases/BackendComplex.svelte:
--------------------------------------------------------------------------------
1 |
56 |
57 | {message}
58 |
59 |
60 | {#each listData as data}
61 |
62 |
63 |
(selected = data)}
66 | class:active={selected?.id === data.id}
67 | >
68 |
{data.label}
69 |
{data.description || ""}
70 |
71 | {/each}
72 |
73 |
74 | {#if selected}
75 |
76 | {/if}
77 |
78 |
102 |
--------------------------------------------------------------------------------
/svelte/demos/cases/BasicInit.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
Normal
11 |
12 |
13 |
14 |
15 |
16 |
Readonly
17 |
18 |
19 |
20 |
21 |
22 |
23 |
39 |
--------------------------------------------------------------------------------
/svelte/demos/cases/Batches.svelte:
--------------------------------------------------------------------------------
1 |
25 |
26 |
27 |
28 |
Segmented
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
Tabbar
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
Toolbar
49 |
50 |
51 | {
59 | if (item.key === "batch") activeBatch = value;
60 | }}
61 | />
62 |
63 |
64 |
65 |
66 |
67 |
86 |
--------------------------------------------------------------------------------
/svelte/demos/cases/ChangesAuto.svelte:
--------------------------------------------------------------------------------
1 |
37 |
38 | {message}
39 |
40 |
41 | {#each listData as data (data.id)}
42 |
43 |
44 |
(selected = data)}
47 | class:active={selected?.id === data.id}
48 | >
49 |
{data.label}
50 |
{data.description || ""}
51 |
starts on: {data.start_date?.toLocaleDateString() || ""}
52 |
53 | {/each}
54 |
55 |
56 | {#if selected}
57 |
65 | {/if}
66 |
67 |
91 |
--------------------------------------------------------------------------------
/svelte/demos/cases/ChangesConfirmed.svelte:
--------------------------------------------------------------------------------
1 |
37 |
38 | {message}
39 |
40 |
41 | {#each listData as data}
42 |
43 |
44 |
(selected = data)}
47 | class:active={selected?.id === data.id}
48 | >
49 |
{data.label}
50 |
{data.description || ""}
51 |
starts on: {data.start_date?.toLocaleDateString() || ""}
52 |
53 | {/each}
54 |
55 |
56 | {#if selected}
57 |
65 | {/if}
66 |
67 |
91 |
--------------------------------------------------------------------------------
/svelte/demos/cases/Comments.svelte:
--------------------------------------------------------------------------------
1 |
42 |
43 | {message}
44 |
45 |
46 | {#each listData as data}
47 |
48 |
49 |
select(data.id)}
52 | class:active={selected?.id === data.id}
53 | >
54 |
{data.label}
55 |
{data.description || ""}
56 |
starts on: {data.start_date.toLocaleDateString()}
57 |
58 | {/each}
59 |
60 |
61 | {#if selected}
62 |
71 | {/if}
72 |
73 |
97 |
--------------------------------------------------------------------------------
/svelte/demos/cases/CustomButtons.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 | {message}
15 |
16 |
17 |
None
18 |
19 |
20 |
21 |
22 |
23 |
Top Bar
24 |
25 |
26 |
27 |
28 |
29 |
Top Bar, Right
30 |
31 |
42 |
43 |
44 |
45 |
Top Bar, Left
46 |
47 |
62 |
63 |
64 |
65 |
Top Bar, Mixed
66 |
67 |
85 |
86 |
87 |
88 |
Bottom Bar
89 |
90 |
106 |
107 |
108 |
109 |
110 |
134 |
--------------------------------------------------------------------------------
/svelte/demos/cases/Gantt.svelte:
--------------------------------------------------------------------------------
1 |
169 |
170 | Imitation of gantt project progress
171 |
172 |
173 | {#each tasks as data}
174 |
175 |
176 |
selectActive(data)}
179 | class:active={activeTask?.id === data.id}
180 | >
181 |
{data.text}
182 |
183 | {/each}
184 |
185 |
186 | {#if activeTask}
187 |
217 | {/if}
218 |
219 |
240 |
--------------------------------------------------------------------------------
/svelte/demos/cases/Kanban.svelte:
--------------------------------------------------------------------------------
1 |
134 |
135 | Imitation of kanban cards
136 |
137 |
138 | {#each cards as data}
139 |
140 |
141 |
(activeCard = data)}
144 | class:active={activeCard?.id === data.id}
145 | >
146 |
{data.label}
147 |
148 | {/each}
149 |
150 |
151 | {#if activeCard}
152 |
170 | {/if}
171 |
172 |
192 |
--------------------------------------------------------------------------------
/svelte/demos/cases/Layouts.svelte:
--------------------------------------------------------------------------------
1 |
119 |
120 |
161 |
162 | {#if visible}
163 | (visible = false)}
167 | />
168 | {/if}
169 |
170 |
180 |
--------------------------------------------------------------------------------
/svelte/demos/cases/Locales.svelte:
--------------------------------------------------------------------------------
1 |
52 |
53 |
54 |
55 | {
60 | readonly = false;
61 | visible = true;
62 | }}
63 | />
64 | {
69 | readonly = true;
70 | visible = true;
71 | }}
72 | />
73 |
74 |
75 | {#key lang}
76 |
77 |
78 | {#if visible}
79 |
86 | {/if}
87 |
88 | {/key}
89 |
90 |
91 |
101 |
--------------------------------------------------------------------------------
/svelte/demos/cases/ModalColumns.svelte:
--------------------------------------------------------------------------------
1 |
21 |
22 | {message}
23 |
24 |
67 |
68 | {#if visible}
69 |
78 | {/if}
79 |
80 |
97 |
--------------------------------------------------------------------------------
/svelte/demos/cases/ModalPanel.svelte:
--------------------------------------------------------------------------------
1 |
20 |
21 | {message}
22 |
23 |
60 |
61 | {#if visible}
62 |
70 | {/if}
71 |
72 |
89 |
--------------------------------------------------------------------------------
/svelte/demos/cases/MultipleCombos.svelte:
--------------------------------------------------------------------------------
1 |
76 |
77 |
78 |
Changing the country updates list of options in the cities combo
79 |
80 |
81 |
82 |
83 |
84 |
96 |
--------------------------------------------------------------------------------
/svelte/demos/cases/NotImplemented.svelte:
--------------------------------------------------------------------------------
1 |
2 |
Not implemented yet
3 |
4 |
5 |
15 |
--------------------------------------------------------------------------------
/svelte/demos/cases/Overflow.svelte:
--------------------------------------------------------------------------------
1 |
94 |
95 |
156 |
157 | Inline Editor example
158 |
159 |
160 |
161 |
162 | {#if visible}
163 | (visible = false)}
167 | />
168 | {/if}
169 |
170 |
192 |
--------------------------------------------------------------------------------
/svelte/demos/cases/RequiredFields.svelte:
--------------------------------------------------------------------------------
1 |
61 |
62 |
63 |
Editor with required fields
64 |
65 |
81 |
82 |
83 |
84 |
92 |
--------------------------------------------------------------------------------
/svelte/demos/cases/SaveValidatedValues.svelte:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
26 | (visible = true)} class:active={visible}>
27 | {#each Object.values(values) as value}
28 | {value}
29 | {/each}
30 |
31 |
32 | {#if visible}
33 |
41 | {/if}
42 |
43 |
60 |
--------------------------------------------------------------------------------
/svelte/demos/cases/Scheduler.svelte:
--------------------------------------------------------------------------------
1 |
227 |
228 | Imitation of scheduler events
229 |
230 |
231 | {#each events as data}
232 |
233 |
234 |
selectActive(data)}
237 | class:active={activeEvent?.id === data.id}
238 | >
239 |
{data.text}
240 |
241 | {/each}
242 |
243 |
244 | {#if activeEvent}
245 |
275 | {/if}
276 |
277 |
298 |
--------------------------------------------------------------------------------
/svelte/demos/cases/SidebarPanel.svelte:
--------------------------------------------------------------------------------
1 |
20 |
21 | {message}
22 |
23 |
63 |
64 | {#if visible}
65 |
73 | {/if}
74 |
75 |
92 |
--------------------------------------------------------------------------------
/svelte/demos/cases/SubPanels.svelte:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
Normal sections
22 |
23 |
24 |
25 |
Exclusive mode
26 |
27 |
28 |
29 |
Normal sections
30 |
31 |
32 |
33 |
Accordion mode
34 |
35 |
36 |
37 |
38 |
58 |
--------------------------------------------------------------------------------
/svelte/demos/cases/Tasklist.svelte:
--------------------------------------------------------------------------------
1 |
39 |
40 | {message}
41 |
42 |
43 | {#each listData as data}
44 |
45 |
46 |
select(data.id)}
49 | class:active={selected?.id === data.id}
50 | >
51 |
{data.label}
52 |
{data.description || ""}
53 |
starts on: {data.start_date.toLocaleDateString()}
54 |
55 | {/each}
56 |
57 |
58 | {#if selected}
59 |
67 | {/if}
68 |
69 |
93 |
--------------------------------------------------------------------------------
/svelte/demos/cases/ToolbarValues.svelte:
--------------------------------------------------------------------------------
1 |
42 |
43 | {message}
44 |
45 |
46 | {#each listData as data}
47 |
48 |
49 |
select(data.id)}
52 | class:active={selected?.id === data.id}
53 | >
54 |
{data.label}
55 |
{data.description || ""}
56 | {#if data.state}
57 |
State: {data.state}
58 | {/if}
59 |
60 | {/each}
61 |
62 |
63 | {#if selected}
64 |
79 | {/if}
80 |
81 |
105 |
--------------------------------------------------------------------------------
/svelte/demos/cases/Validation.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
Editor with validation and error messages
10 |
11 |
12 |
13 |
14 |
15 |
22 |
--------------------------------------------------------------------------------
/svelte/demos/common/Index.svelte:
--------------------------------------------------------------------------------
1 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
120 |
121 |
(show = false)}
127 | >
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
323 |
--------------------------------------------------------------------------------
/svelte/demos/common/Link.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 | {data[1]}
10 |
11 |
12 |
33 |
--------------------------------------------------------------------------------
/svelte/demos/common/ListRoutes.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
7 | {#each routes as route}
8 | {'"' + route + '",\n'}
9 | {/each}
10 |
12 |
13 |
18 |
--------------------------------------------------------------------------------
/svelte/demos/common/Router.svelte:
--------------------------------------------------------------------------------
1 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/svelte/demos/common/helpers.js:
--------------------------------------------------------------------------------
1 | import { push } from "svelte-spa-router";
2 | import { wrap } from "svelte-spa-router/wrap";
3 | import { links as raw } from "../routes";
4 |
5 | const routes = {
6 | "/": wrap({
7 | component: {},
8 | conditions: () => {
9 | push("/base/willow");
10 | return false;
11 | },
12 | }),
13 | };
14 |
15 | function getRoutes(skinSettings, cb) {
16 | raw.forEach(
17 | a =>
18 | (routes[a[0]] = wrap({
19 | component: a[2],
20 | userData: a,
21 | props: { ...skinSettings },
22 | conditions: x => {
23 | cb(x.location);
24 | return true;
25 | },
26 | }))
27 | );
28 |
29 | return routes;
30 | }
31 |
32 | function getLinks() {
33 | return raw;
34 | }
35 |
36 | export { push, getRoutes, getLinks };
37 |
--------------------------------------------------------------------------------
/svelte/demos/custom/gantt/Links.svelte:
--------------------------------------------------------------------------------
1 |
24 |
25 | {#if value}
26 | {#each value as links}
27 | {#if links.data.length}
28 |
29 |
30 |
31 |
32 |
33 | {#each links.data as obj}
34 |
35 |
36 |
37 |
38 | {obj.task.text || ""}
39 |
40 |
41 |
42 |
43 |
44 |
49 | handleChange(e, obj.link.id)}
50 | >
51 | {#snippet children({ option })}
52 | {option.label}
53 | {/snippet}
54 |
55 |
56 |
57 |
58 |
59 |
64 |
65 |
66 | {/each}
67 |
68 |
69 |
70 | {/if}
71 | {/each}
72 | {/if}
73 |
74 |
114 |
--------------------------------------------------------------------------------
/svelte/demos/custom/kanban/PriorityCombo.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 | {#snippet children({ option })}
10 |
11 | {#if option.color}
12 |
13 | {:else if option.avatar || option.avatarColor}
14 |
15 | {/if}
16 | {option.label}
17 |
18 | {/snippet}
19 |
20 |
21 |
33 |
--------------------------------------------------------------------------------
/svelte/demos/custom/kanban/UserIcon.svelte:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 | {#if data.avatar}
26 |
27 | {:else if noTransform}{data.label}{:else}{firstLetters}{/if}
28 |
29 |
30 |
66 |
--------------------------------------------------------------------------------
/svelte/demos/custom/kanban/UserMultiselect.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 | {#snippet children({ option })}
11 |
12 |
13 |
14 | {option.label}
15 |
16 |
17 | {/snippet}
18 |
19 |
20 |
30 |
--------------------------------------------------------------------------------
/svelte/demos/custom/scheduler/ColorPickerSchema.svelte:
--------------------------------------------------------------------------------
1 |
27 |
28 |
34 |
--------------------------------------------------------------------------------
/svelte/demos/custom/scheduler/Combo.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | {#snippet children({ option })}
9 | {@const Component = template}
10 |
11 | {/snippet}
12 |
13 |
--------------------------------------------------------------------------------
/svelte/demos/custom/scheduler/DateTimePicker.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 | {#if time}
12 |
13 | {/if}
14 |
15 |
16 |
33 |
--------------------------------------------------------------------------------
/svelte/demos/custom/scheduler/Multiselect.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | {#snippet children({ option })}
9 | {@const Component = template}
10 |
11 |
12 |
13 | {/snippet}
14 |
15 |
--------------------------------------------------------------------------------
/svelte/demos/custom/scheduler/Uploader.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
39 |
--------------------------------------------------------------------------------
/svelte/demos/custom/scheduler/templates/ComboOption.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
{option.label}
7 | {#if option.color}
8 |
12 | {/if}
13 |
14 |
15 |
27 |
--------------------------------------------------------------------------------
/svelte/demos/custom/scheduler/templates/MultiselectOption.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 | {option.label}
8 |
9 |
10 |
23 |
--------------------------------------------------------------------------------
/svelte/demos/data.js:
--------------------------------------------------------------------------------
1 | export function getData() {
2 | const start_date = new Date("01/05/2021");
3 | const end_date = new Date("01/15/2021");
4 |
5 | const items = [
6 | { comp: "text", key: "name", label: "Name", column: "left" },
7 | { comp: "checkbox", key: "admin", label: "Is Admin" },
8 | { comp: "text", key: "email", label: "Email" },
9 | {
10 | comp: "textarea",
11 | key: "descr",
12 | label: "Description",
13 | column: "left",
14 | },
15 | ];
16 |
17 | const batchItems = [
18 | {
19 | comp: "text",
20 | key: "name",
21 | batch: "main",
22 | label: "Name",
23 | },
24 | {
25 | comp: "textarea",
26 | key: "descr",
27 | batch: "main",
28 | label: "Description",
29 | },
30 | {
31 | comp: "text",
32 | key: "email",
33 | batch: "main",
34 | label: "Email",
35 | },
36 | {
37 | comp: "checkbox",
38 | key: "admin",
39 | batch: "cfg",
40 | label: "Is Admin",
41 | },
42 | {
43 | key: "theme",
44 | batch: "cfg",
45 | comp: "checkbox",
46 | label: "Dark theme",
47 | },
48 | ];
49 |
50 | const values = {
51 | name: "John Doe",
52 | descr: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.",
53 | admin: true,
54 | email: "john.doe@example.org",
55 | theme: false,
56 | comments: [],
57 | tasks: [],
58 | };
59 |
60 | const users = [
61 | {
62 | id: 1,
63 | name: "Sarah Smith",
64 | label: "Sarah Smith",
65 | },
66 | {
67 | id: 2,
68 | name: "Diego Redmoor",
69 | label: "Diego Redmoor",
70 | },
71 | {
72 | id: 3,
73 | name: "Alex Johnson",
74 | label: "Alex Johnson",
75 | },
76 | {
77 | id: 4,
78 | name: "Marta Kowalski",
79 | label: "Marta Kowalski",
80 | },
81 | ];
82 |
83 | const listItems = [
84 | {
85 | key: "label",
86 | comp: "text",
87 | label: "Label",
88 | },
89 | {
90 | key: "description",
91 | comp: "textarea",
92 | label: "Description",
93 | },
94 | {
95 | comp: "combo",
96 | label: "Priority",
97 | key: "priority",
98 | options: [
99 | { id: 1, label: "High" },
100 | { id: 2, label: "Medium" },
101 | { id: 3, label: "Low" },
102 | ],
103 | },
104 | {
105 | comp: "color",
106 | label: "Color",
107 | key: "color",
108 | },
109 | {
110 | comp: "slider",
111 | key: "progress",
112 | label: "Progress",
113 | },
114 | {
115 | comp: "date",
116 | key: "start_date",
117 | label: "Start date",
118 | },
119 | {
120 | comp: "date",
121 | key: "end_date",
122 | label: "End date",
123 | },
124 | {
125 | comp: "multiselect",
126 | key: "users",
127 | label: "Users",
128 | options: users,
129 | },
130 | {
131 | comp: "hidden",
132 | key: "state",
133 | },
134 | ];
135 |
136 | const listData = [
137 | {
138 | id: 1,
139 | label: "Integration with React",
140 | priority: 1,
141 | color: "#65D3B3",
142 | description:
143 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
144 |
145 | start_date,
146 | end_date,
147 |
148 | progress: 25,
149 | users: [1, 2, 3, 4],
150 | sprint: "1.0",
151 | column: "backlog",
152 | type: "feature",
153 | comments: [
154 | {
155 | id: 1,
156 | user: 1,
157 | cardId: 1,
158 | content:
159 | "Greetings, fellow colleagues. I would like to share my insights on this task. I reckon we should deal with at least half of the points in the plan without further delays.",
160 | date: new Date(2025, 2, 1, 10, 35),
161 | },
162 | {
163 | id: 2,
164 | user: 2,
165 | cardId: 1,
166 | content:
167 | "Hi, Diego. I am sure that that's exactly what is thought best out there in Dunwall. Let's just do what we are supposed to do to get the result.",
168 | date: new Date(2025, 2, 1, 10, 57),
169 | },
170 | {
171 | id: 5,
172 | user: 3,
173 | cardId: 1,
174 | content:
175 | "Absolutely, Diego. Action speaks louder than words, and in this case, it's about executing the plan efficiently. Let's prioritize tasks and tackle them head-on.",
176 | date: new Date(2025, 2, 1, 11, 13),
177 | },
178 | {
179 | id: 6,
180 | user: 2,
181 | cardId: 1,
182 | content:
183 | "I couldn't agree more, Sarah. Time is of the essence, and we can't afford to waste any more of it. Let's dive into the plan and start ticking off those points one by one.",
184 | date: new Date(2025, 2, 2, 7, 2),
185 | },
186 | ],
187 | task: [
188 | {
189 | id: 1,
190 | date: new Date(),
191 | content:
192 | "Research best practices for integrating third-party libraries with React",
193 | status: 1,
194 | },
195 | {
196 | id: 2,
197 | date: new Date(),
198 | content:
199 | "Explore modern approaches to building applications using React",
200 | status: 0,
201 | },
202 | {
203 | id: 3,
204 | date: new Date(),
205 | content:
206 | "Explore different methods for integrating React with existing JavaScript frameworks",
207 | status: 0,
208 | },
209 | {
210 | id: 4,
211 | date: new Date(),
212 | content: "Learn about routing in React using React Router",
213 | status: 1,
214 | },
215 | {
216 | id: 5,
217 | date: new Date(),
218 | content:
219 | "Understand principles and best practices for component development in React",
220 | status: 0,
221 | },
222 | {
223 | id: 6,
224 | date: new Date(),
225 | content:
226 | "Explore different methods for integrating React with existing JavaScript frameworks",
227 | status: 0,
228 | },
229 | {
230 | id: 7,
231 | date: new Date(),
232 | content: "Optimize performance in React applications",
233 | status: 0,
234 | },
235 | {
236 | id: 8,
237 | date: new Date(),
238 | content:
239 | "Work with API requests and data handling in React applications",
240 | status: 0,
241 | },
242 | ],
243 | votes: [1, 3, 4],
244 | },
245 | {
246 | id: 2,
247 | label: "Archive the cards/boards ",
248 | priority: 2,
249 | color: "#FFC975",
250 |
251 | start_date,
252 | end_date,
253 | comments: [],
254 | task: [],
255 |
256 | sprint: "1.0",
257 | column: "backlog",
258 | type: "feature",
259 | users: [3, 4],
260 | },
261 | {
262 | id: 3,
263 | label: "Searching and filtering",
264 | priority: 1,
265 | color: "#65D3B3",
266 |
267 | start_date,
268 |
269 | sprint: "1.2",
270 | column: "backlog",
271 | type: "task",
272 | comments: [
273 | {
274 | id: 7,
275 | user: 4,
276 | cardId: 3,
277 | content: "Nice seven",
278 | date: new Date(2025, 2, 5, 8, 24),
279 | },
280 | {
281 | id: 8,
282 | user: 3,
283 | cardId: 3,
284 | content: "Nice eight",
285 | date: new Date(2025, 2, 5, 9, 14),
286 | },
287 | {
288 | id: 9,
289 | user: 1,
290 | cardId: 3,
291 | content: "Nice nine",
292 | date: new Date(2025, 2, 5, 11, 12),
293 | },
294 | ],
295 | task: [
296 | {
297 | id: 1,
298 | date: new Date(),
299 | content:
300 | "Implement basic search functionality to filter items by keyword",
301 | status: 1,
302 | },
303 | {
304 | id: 2,
305 | date: new Date(),
306 | content:
307 | "Add filtering options to categorize items based on specific criteria",
308 | status: 0,
309 | },
310 | ],
311 | votes: [1, 3, 4],
312 | },
313 | ];
314 |
315 | const valuesValidation = {
316 | firstName: "John",
317 | lastName: "Doe",
318 | company: "Innovate company",
319 | contact: "+0122333456",
320 | email: "john.doe@example.org",
321 | additional:
322 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.",
323 | };
324 |
325 | const itemsValidation = [
326 | {
327 | comp: "text",
328 | key: "firstName",
329 | label: "First name",
330 | validation: val => {
331 | const regEx = /^[a-zA-Z]+$/;
332 | return val && regEx.test(val);
333 | },
334 | validationMessage: "Incorrect name!",
335 | },
336 | {
337 | comp: "text",
338 | key: "lastName",
339 | label: "Last name",
340 | validation: val => {
341 | const regEx = /^[a-zA-Z]+$/;
342 | return val && regEx.test(val);
343 | },
344 | validationMessage: "Incorrect name!",
345 | },
346 | {
347 | comp: "text",
348 | key: "company",
349 | label: "Company",
350 | validation: val => val.length > 2,
351 | },
352 | {
353 | comp: "text",
354 | key: "contact",
355 | label: "Contact number",
356 | validation: val => {
357 | const regEx = /^\+?\d*$/;
358 | return val.length > 9 && regEx.test(val);
359 | },
360 | validationMessage: "Please, enter correct phone number",
361 | },
362 | {
363 | comp: "text",
364 | key: "email",
365 | label: "Email",
366 | validation: val => {
367 | //eslint-disable-next-line
368 | const regEx = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g;
369 | return val && regEx.test(val);
370 | },
371 | validationMessage: "Incorrect email!",
372 | },
373 | {
374 | comp: "textarea",
375 | key: "additional",
376 | label: "Additional information",
377 | validation: val => val.length >= 20 && val.length <= 160,
378 | validationMessage:
379 | "Value should have at least 20 characters and no more than 160 characters",
380 | },
381 | ];
382 |
383 | return {
384 | items,
385 | values,
386 | listData,
387 | listItems,
388 | batchItems,
389 | users,
390 | valuesValidation,
391 | itemsValidation,
392 | };
393 | }
394 |
395 | export function sectionItems(sec = {}) {
396 | const data = [
397 | {
398 | comp: "text",
399 | key: "name",
400 | label: "Name",
401 | },
402 | {
403 | comp: "textarea",
404 | key: "descr",
405 | label: "Description",
406 | },
407 | {
408 | comp: "section",
409 | key: "comments-section",
410 | label: "Comments",
411 | },
412 | {
413 | comp: "comments",
414 | key: "comments",
415 | section: "comments-section",
416 | },
417 | {
418 | comp: "section",
419 | key: "tasks-section",
420 | label: "Tasks",
421 | },
422 | {
423 | comp: "tasks",
424 | key: "tasks",
425 | section: "tasks-section",
426 | },
427 | ];
428 |
429 | data.forEach(item => {
430 | if (item.comp === "section") Object.assign(item, sec);
431 | });
432 | return data;
433 | }
434 |
435 | export function onlySectionItems(sec = {}) {
436 | const data = [
437 | {
438 | comp: "section",
439 | key: "personal-section",
440 | label: "Personal Info",
441 | active: true,
442 | },
443 | {
444 | comp: "text",
445 | key: "name",
446 | batch: "main",
447 | label: "Name",
448 | section: "personal-section",
449 | },
450 | {
451 | comp: "textarea",
452 | key: "descr",
453 | batch: "main",
454 | label: "Description",
455 | section: "personal-section",
456 | },
457 | {
458 | comp: "text",
459 | key: "email",
460 | label: "Email",
461 | section: "personal-section",
462 | },
463 |
464 | // the data below is used instead of the commented lines below
465 | {
466 | comp: "section",
467 | key: "role-section",
468 | label: "Role",
469 | },
470 | {
471 | comp: "checkbox",
472 | key: "admin",
473 | label: "Is admin",
474 | section: "role-section",
475 | },
476 | {
477 | comp: "section",
478 | key: "comments-section",
479 | label: "Comments",
480 | },
481 | {
482 | comp: "comments",
483 | key: "comments",
484 | section: "comments-section",
485 | },
486 | {
487 | comp: "section",
488 | key: "tasks-section",
489 | label: "Tasks",
490 | },
491 | {
492 | comp: "tasks",
493 | key: "tasks",
494 | section: "tasks-section",
495 | },
496 | ];
497 |
498 | data.forEach(item => {
499 | if (item.comp === "section") Object.assign(item, sec);
500 | });
501 | return data;
502 | }
503 |
--------------------------------------------------------------------------------
/svelte/demos/index.js:
--------------------------------------------------------------------------------
1 | import { mount } from "svelte";
2 | import Demos from "./common/Index.svelte";
3 |
4 | mount(Demos, {
5 | target: document.querySelector("#wx_demo_area") || document.body,
6 | });
7 |
--------------------------------------------------------------------------------
/svelte/demos/routes.js:
--------------------------------------------------------------------------------
1 | import BasicInit from "./cases/BasicInit.svelte";
2 | import ModalPanel from "./cases/ModalPanel.svelte";
3 | import ModalColumns from "./cases/ModalColumns.svelte";
4 | import SidebarPanel from "./cases/SidebarPanel.svelte";
5 | import Batches from "./cases/Batches.svelte";
6 | import SubPanels from "./cases/SubPanels.svelte";
7 | import ChangesAuto from "./cases/ChangesAuto.svelte";
8 | import ChangesConfirmed from "./cases/ChangesConfirmed.svelte";
9 | import CustomButtons from "./cases/CustomButtons.svelte";
10 | import ActionMenu from "./cases/ActionMenu.svelte";
11 | import ToolbarValues from "./cases/ToolbarValues.svelte";
12 | import Tasklist from "./cases/Tasklist.svelte";
13 | import Comments from "./cases/Comments.svelte";
14 | import Layouts from "./cases/Layouts.svelte";
15 | import BackendComplex from "./cases/BackendComplex.svelte";
16 | import MultipleCombos from "./cases/MultipleCombos.svelte";
17 | import Overflow from "./cases/Overflow.svelte";
18 | import SaveValidatedValues from "./cases/SaveValidatedValues.svelte";
19 | import Validation from "./cases/Validation.svelte";
20 | import RequiredFields from "./cases/RequiredFields.svelte";
21 | import Locales from "./cases/Locales.svelte";
22 | // import Gantt from "./cases/Gantt.svelte";
23 | // import Kanban from "./cases/Kanban.svelte";
24 | // import Scheduler from "./cases/Scheduler.svelte";
25 |
26 | export const links = [
27 | ["/base/:skin", "Basic Form", BasicInit],
28 | ["/modal/:skin", "Modal Editor", ModalPanel],
29 | ["/columns/:skin", "Modal Editor with columns", ModalColumns],
30 | ["/sidebar/:skin", "Sidebar Editor", SidebarPanel],
31 | ["/changes-auto/:skin", "Changes: auto mode", ChangesAuto],
32 | ["/changes-confirmed/:skin", "Changes: confirmation", ChangesConfirmed],
33 | ["/batches/:skin", "Batches", Batches],
34 | ["/subpanels/:skin", "Sub panels", SubPanels],
35 | ["/actions/:skin", "Action menu", ActionMenu],
36 | ["/buttons/:skin", "Custom buttons", CustomButtons],
37 | ["/comments/:skin", "Comments", Comments],
38 | ["/taslkist/:skin", "Tasklist", Tasklist],
39 | ["/layouts/:skin", "Layouts", Layouts],
40 | ["/backend-complex/:skin", "Separate backends", BackendComplex],
41 | ["/combos/:skin", "Multiple combos", MultipleCombos],
42 | ["/overflow/:skin", "Overflow", Overflow],
43 | ["/toolbar/:skin", "Toolbar values", ToolbarValues],
44 | ["/required/:skin", "Required fields", RequiredFields],
45 | ["/validation/:skin", "Validation", Validation],
46 | ["/validation-save/:skin", "Save validated values", SaveValidatedValues],
47 | ["/locales/:skin", "Locales", Locales],
48 | // ["/gantt/:skin", "Gantt", Gantt],
49 | // ["/kanban/:skin", "Kanban", Kanban],
50 | // ["/scheduler/:skin", "Scheduler", Scheduler],
51 | ];
52 |
--------------------------------------------------------------------------------
/svelte/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Svelte Widgets
7 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/svelte/license.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 XB Software Sp. z o.o.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/svelte/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wx-svelte-editor",
3 | "version": "2.1.2",
4 | "description": "Svelte component for creating forms to edit structured data on a page (info cards, text blocks, table rows, etc",
5 | "keywords": [
6 | "svelte",
7 | "ui component",
8 | "editor",
9 | "form",
10 | "editing form",
11 | "data editing"
12 | ],
13 | "productTag": "editor",
14 | "productTrial": false,
15 | "type": "module",
16 | "scripts": {
17 | "build": "vite build",
18 | "build:dist": "vite build --mode dist",
19 | "build:tests": "vite build --mode test",
20 | "lint": "yarn eslint ./demos ./src",
21 | "start": "vite --open",
22 | "start:tests": "vite --open=/tests/ --host 0.0.0.0 --port 5100 --mode test",
23 | "test": "true",
24 | "test:cypress": "cypress run -P ./ --config \"baseUrl=http://localhost:5100/tests\""
25 | },
26 | "svelte": "src/index.js",
27 | "exports": {
28 | ".": {
29 | "svelte": "./src/index.js"
30 | },
31 | "./package.json": "./package.json"
32 | },
33 | "license": "MIT",
34 | "repository": {
35 | "type": "git",
36 | "url": "https://github.com/svar-widgets/editor.git"
37 | },
38 | "bugs": {
39 | "url": "https://forum.svar.dev"
40 | },
41 | "homepage": "https://svar.dev/svelte/editor/",
42 | "dependencies": {
43 | "wx-editor-locales": "2.1.2",
44 | "wx-lib-dom": "0.8.0",
45 | "wx-lib-state": "1.9.0",
46 | "wx-lib-svelte": "0.5.1",
47 | "wx-svelte-comments": "2.1.1",
48 | "wx-svelte-core": "2.1.1",
49 | "wx-svelte-tasklist": "2.1.1",
50 | "wx-svelte-toolbar": "2.1.1"
51 | },
52 | "files": [
53 | "src",
54 | "readme.md",
55 | "whatsnew.md",
56 | "license.txt"
57 | ]
58 | }
59 |
--------------------------------------------------------------------------------
/svelte/postcss.config.js:
--------------------------------------------------------------------------------
1 | import autoprefixer from "autoprefixer";
2 |
3 | const plugins = [autoprefixer];
4 | export { plugins };
5 |
--------------------------------------------------------------------------------
/svelte/readme.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # SVAR Svelte Editor
4 |
5 | [](https://www.npmjs.com/package/wx-svelte-editor)
6 | [](https://github.com/svar-widgets/editor/blob/main/license.txt)
7 | [](https://www.npmjs.com/package/wx-svelte-editor)
8 |
9 |
10 |
11 |
12 |
13 | [Documentation](https://docs.svar.dev/svelte/editor/) • [Demos](https://docs.svar.dev/svelte/editor/samples/#/base/willow)
14 |
15 |
16 |
17 | An intuitive Svelte component for creating content editing forms to manage data within UI elements on a page. You can use it for editing structured data like table rows, informational cards, blocks with text, etc.
18 |
19 | ### Key features:
20 |
21 | - **Flexible display options**: open the editor in a modal popup or as a seamless sidebar for convenient access.
22 | - **Multiple input types**: Use various input fields like text inputs, checkboxes, date pickers, sliders, and more controls from [SVAR Svelte Core](https://svar.dev/svelte/core/) library.
23 | - **Built-in validation**: Includes basic validation for required fields and supports custom validation rules for advanced scenarios.
24 | - **Flexible save options**: Choose between manual saves, auto-save, or custom saving logic adjusted to your needs.
25 | - **Compact layout**: Organize forms into expandable sections or a 2-column layout for efficient use of screen space.
26 |
27 | ### How to Use
28 |
29 | To use the widget, simply import the package and include the component in your Svelte file:
30 |
31 | ```svelte
32 |
37 |
38 |
39 | ```
40 |
41 | ### How to Modify
42 |
43 | Typically, you don't need to modify the code. However, if you wish to do so, follow these steps:
44 |
45 | 1. Run `yarn` to install dependencies. Note that this project is a monorepo using `yarn` workspaces, so npm will not work
46 | 2. Start the project in development mode with `yarn start`
47 |
48 | ### Run Tests
49 |
50 | To run the test:
51 |
52 | 1. Start the test examples with:
53 | ```sh
54 | yarn start:tests
55 | ```
56 | 2. In a separate console, run the end-to-end tests with:
57 | ```sh
58 | yarn test:cypress
59 | ```
60 |
--------------------------------------------------------------------------------
/svelte/src/components/Columns.svelte:
--------------------------------------------------------------------------------
1 |
32 |
33 | {#if layout === "columns"}
34 |
35 |
36 |
37 |
38 |
39 |
47 |
48 |
49 | {:else}
50 |
51 | {/if}
52 |
53 |
72 |
--------------------------------------------------------------------------------
/svelte/src/components/Editor.svelte:
--------------------------------------------------------------------------------
1 |
26 |
27 |
28 |
44 | {#if children}{@render children()}{/if}
45 |
46 |
47 |
--------------------------------------------------------------------------------
/svelte/src/components/Form.svelte:
--------------------------------------------------------------------------------
1 |
42 |
43 |
44 |
45 |
56 | {#if children}{@render children()}{/if}
57 |
58 |
59 |
60 |
61 |
67 |
--------------------------------------------------------------------------------
/svelte/src/components/FormEditor.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | {#if children}{@render children()}{/if}
19 | {#each editors.config as editor}
20 | {#if !editor.hidden}
21 | {#if editor.comp === "readonly" || editor.comp === "section"}
22 | {@const Component = getItemHandler(editor.comp)}
23 |
29 | {:else}
30 |
31 |
37 | {#snippet children({ id })}
38 | {@const Component2 = getItemHandler(editor.comp)}
39 |
41 | onchange &&
42 | onchange({
43 | value: ev.value,
44 | key: editor.key,
45 | })}
46 | {...editor}
47 | {id}
48 | label={undefined}
49 | error={errors && errors[editor.key]}
50 | value={data[editor.key]}
51 | />
52 | {/snippet}
53 |
54 | {#if errors && errors[editor.key] && editor.validationMessage}
55 |
{editor.validationMessage}
56 | {/if}
57 |
58 | {/if}
59 | {/if}
60 | {/each}
61 |
62 |
63 |
77 |
--------------------------------------------------------------------------------
/svelte/src/components/Helpers.svelte.js:
--------------------------------------------------------------------------------
1 | export function link(getValue) {
2 | let localState = $state(undefined);
3 | const linkedDerived = $derived.by(() => {
4 | const linkedValue = getValue();
5 | return typeof localState !== "undefined" ? localState : linkedValue;
6 | });
7 |
8 | return [
9 | () => linkedDerived,
10 | v => {
11 | localState = v;
12 | },
13 | ];
14 | }
15 |
16 | export function dataLink(getValue) {
17 | const [getErrors, setErrors] = link(getValue);
18 |
19 | return {
20 | get errors() {
21 | return getErrors();
22 | },
23 | set errors(value) {
24 | setErrors(value);
25 | },
26 | };
27 | }
28 |
--------------------------------------------------------------------------------
/svelte/src/components/Layout.svelte:
--------------------------------------------------------------------------------
1 |
76 |
77 | {#if placement === "modal"}
78 |
79 |
80 |
87 |
91 | {#if children}{@render children()}{/if}
92 |
101 |
109 |
110 |
111 |
112 | {:else if placement === "sidebar"}
113 |
114 |
115 |
122 |
126 | {#if children}{@render children()}{/if}
127 |
136 |
144 |
145 |
146 |
147 | {:else}
148 |
171 | {/if}
172 |
173 |
199 |
--------------------------------------------------------------------------------
/svelte/src/components/Values.svelte:
--------------------------------------------------------------------------------
1 |
171 |
172 |
173 |
174 |
189 | {#if children}{@render children()}{/if}
190 |
191 |
--------------------------------------------------------------------------------
/svelte/src/components/buttons/Toolbar.svelte:
--------------------------------------------------------------------------------
1 |
15 |
16 | {#if items.length}
17 |
18 |
24 |
25 | {/if}
26 |
27 |
48 |
--------------------------------------------------------------------------------
/svelte/src/components/buttons/buttons.js:
--------------------------------------------------------------------------------
1 | export const Spacer = () => ({ comp: "spacer" });
2 | export const CancelButton = _ => ({
3 | comp: "button",
4 | text: _("Cancel"),
5 | id: "cancel",
6 | });
7 | export const SaveButton = _ => ({
8 | type: "primary",
9 | comp: "button",
10 | text: _("Save"),
11 | id: "save",
12 | });
13 | export const CloseIcon = () => ({
14 | comp: "icon",
15 | icon: "wxi-close",
16 | id: "close",
17 | });
18 |
--------------------------------------------------------------------------------
/svelte/src/components/sections/ReadOnly.svelte:
--------------------------------------------------------------------------------
1 |
25 |
26 | {#if text}
27 | {text}
28 | {/if}
29 |
--------------------------------------------------------------------------------
/svelte/src/components/sections/Section.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
11 | onclick &&
12 | onclick({ item: { id: "toggle-section", key: active ? null : key } })}
13 | >
14 |
{label}
15 |
16 |
17 |
18 |
37 |
--------------------------------------------------------------------------------
/svelte/src/editor.js:
--------------------------------------------------------------------------------
1 | import { uid, isSame } from "wx-lib-state";
2 |
3 | function rawGetter(key) {
4 | if (typeof key === "string" && key.includes(".")) {
5 | const parts = key.split(".");
6 | return obj => {
7 | let out = obj;
8 | parts.forEach(p => {
9 | out = out[p];
10 | });
11 | return out;
12 | };
13 | }
14 |
15 | return obj => obj[key];
16 | }
17 | function rawSetter(key) {
18 | if (typeof key === "string" && key.includes(".")) {
19 | const parts = key.split(".");
20 | return (obj, v) => {
21 | let out = obj;
22 | parts.forEach((p, i) => {
23 | if (i === parts.length - 1) out[p] = v;
24 | else out = out[p];
25 | });
26 | };
27 | }
28 |
29 | return (obj, v) => (obj[key] = v);
30 | }
31 |
32 | export function itemsToEditors(sections) {
33 | const config = sections.map(s => {
34 | const obj = { ...s };
35 | if (s.config) Object.assign(obj, s.config);
36 |
37 | obj.key = s.key || uid();
38 | obj.setter = s.setter || rawSetter(s.key);
39 | obj.getter = s.getter || rawGetter(s.key);
40 | return obj;
41 | });
42 |
43 | const getValues = raw => {
44 | const out = {};
45 | config.forEach(ed => {
46 | if (ed.comp === "section") return;
47 | if (ed.getter) out[ed.key] = ed.getter(raw);
48 | else out[ed.key] = raw[ed.key];
49 | });
50 | return out;
51 | };
52 |
53 | const setValues = (out, values, changes) => {
54 | const fields = changes.length
55 | ? changes.map(key => config.find(a => a.key === key))
56 | : config;
57 | fields.forEach(ed => {
58 | if (ed.setter) ed.setter(out, values[ed.key]);
59 | else out[ed.key] = values[ed.key];
60 | });
61 |
62 | return out;
63 | };
64 |
65 | const diff = (raw, values) => {
66 | const initial = getValues(raw);
67 | const changes = [];
68 | config.forEach(ed => {
69 | const a = initial[ed.key];
70 | const b = values[ed.key];
71 | // we can have a situation when initial value is undefined
72 | // but empty editor will return empty value instead of undefined
73 | // this behavior is correct, but we need to ignore this case
74 | if (!isSame(a, b) && (a !== undefined || !!b)) {
75 | changes.push(ed.key);
76 | }
77 | });
78 |
79 | return changes;
80 | };
81 |
82 | const validateValues = (values, _) => {
83 | let any = 0;
84 | const errors = {};
85 |
86 | config.forEach(ed => {
87 | if (ed.required && !values[ed.key]) {
88 | errors[ed.key] = {
89 | errorType: "required",
90 | };
91 | ed.validationMessage =
92 | ed.validationMessage || _("This field is required");
93 | any++;
94 | } else if (ed.validation && !ed.validation(values[ed.key])) {
95 | errors[ed.key] = {
96 | errorType: "validation",
97 | };
98 | ed.validationMessage =
99 | ed.validationMessage || _("Invalid value");
100 | any++;
101 | }
102 | });
103 |
104 | return any > 0 ? errors : null;
105 | };
106 |
107 | return {
108 | config: config.filter(x => x.comp !== "hidden"),
109 | getValues,
110 | setValues,
111 | diff,
112 | validateValues,
113 | };
114 | }
115 |
--------------------------------------------------------------------------------
/svelte/src/en.js:
--------------------------------------------------------------------------------
1 | export default {
2 | editor: {},
3 | };
4 |
--------------------------------------------------------------------------------
/svelte/src/helpers.js:
--------------------------------------------------------------------------------
1 | const handlers = {};
2 | export function getItemHandler(type) {
3 | return handlers[type] || handlers["text"];
4 | }
5 | export function registerEditorItem(type, handler) {
6 | handlers[type] = handler;
7 | }
8 |
--------------------------------------------------------------------------------
/svelte/src/index.js:
--------------------------------------------------------------------------------
1 | import { registerEditorItem } from "./helpers";
2 | import Form from "./components/Form.svelte";
3 | import Editor from "./components/Editor.svelte";
4 |
5 | import Material from "./themes/Material.svelte";
6 | import Willow from "./themes/Willow.svelte";
7 | import WillowDark from "./themes/WillowDark.svelte";
8 |
9 | import ReadOnly from "./components/sections/ReadOnly.svelte";
10 | import Section from "./components/sections/Section.svelte";
11 | import { Text } from "wx-svelte-core";
12 | import { TextArea } from "wx-svelte-core";
13 | import { Checkbox } from "wx-svelte-core";
14 |
15 | registerEditorItem("text", Text);
16 | registerEditorItem("textarea", TextArea);
17 | registerEditorItem("checkbox", Checkbox);
18 |
19 | registerEditorItem("readonly", ReadOnly);
20 | registerEditorItem("section", Section);
21 |
22 | import { setEnv } from "wx-lib-dom";
23 | import { env } from "wx-lib-svelte";
24 | setEnv(env);
25 |
26 | export { registerEditorItem, Form, Editor, Material, Willow, WillowDark };
27 |
--------------------------------------------------------------------------------
/svelte/src/themes/Material.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 | {#if children}
7 | {@render children()}
8 | {:else}
9 |
10 | {/if}
11 |
12 |
17 |
--------------------------------------------------------------------------------
/svelte/src/themes/Willow.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 | {#if children}
7 | {@render children()}
8 | {:else}
9 |
10 | {/if}
11 |
12 |
17 |
--------------------------------------------------------------------------------
/svelte/src/themes/WillowDark.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 | {#if children}
7 | {@render children()}
8 | {:else}
9 |
10 | {/if}
11 |
12 |
17 |
--------------------------------------------------------------------------------
/svelte/svelte.config.js:
--------------------------------------------------------------------------------
1 | import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
2 |
3 | export default {
4 | // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
5 | // for more information about preprocessors
6 | preprocess: vitePreprocess(),
7 | };
8 |
--------------------------------------------------------------------------------
/svelte/tests/Index.svelte:
--------------------------------------------------------------------------------
1 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
98 |
--------------------------------------------------------------------------------
/svelte/tests/cases/LocalData.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/svelte/tests/data.js:
--------------------------------------------------------------------------------
1 | export function getData() {
2 | return {};
3 | }
4 |
--------------------------------------------------------------------------------
/svelte/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Svelte Widgets
7 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/svelte/tests/index.js:
--------------------------------------------------------------------------------
1 | import Demos from "./Index.svelte";
2 | import { mount } from "svelte";
3 |
4 | mount(Demos, {
5 | target: document.querySelector("#wx_demo_area") || document.body,
6 | });
7 |
--------------------------------------------------------------------------------
/svelte/tests/routes.js:
--------------------------------------------------------------------------------
1 | import LocalData from "./cases/LocalData.svelte";
2 |
3 | export const links = [["/local-data", "", LocalData]];
4 |
--------------------------------------------------------------------------------
/svelte/vite.config.js:
--------------------------------------------------------------------------------
1 | import { loadEnv } from "vite";
2 | import { resolve } from "path";
3 | import { existsSync } from "fs";
4 | import { svelte } from "@sveltejs/vite-plugin-svelte";
5 | import { visualizer } from "rollup-plugin-visualizer";
6 | import { waitChanges, waitOn } from "wx-vite-tools";
7 | import conditionalCompile from "vite-plugin-conditional-compile";
8 | import pkg from "./package.json" with { type: "json" };
9 |
10 | export default async ({ mode }) => {
11 | process.env = { ...process.env, ...loadEnv(mode, process.cwd(), "WX") };
12 | const files = [];
13 |
14 | if (mode !== "production") {
15 | const paths = [
16 | resolve(__dirname, "../store/dist/index.js"),
17 | resolve(__dirname, "../provider/dist/index.js"),
18 | ];
19 |
20 | paths.forEach(path => {
21 | if (existsSync(path)) {
22 | files.push(path);
23 | }
24 | });
25 | }
26 |
27 | const plugins = [];
28 |
29 | if (files.length) plugins.push(waitChanges({ files }));
30 | if (mode !== "development") plugins.push(conditionalCompile());
31 | plugins.push(svelte({}));
32 |
33 | const name = pkg.productTag;
34 |
35 | let build,
36 | publicDir = resolve(__dirname, "public"),
37 | server = {},
38 | base = "";
39 |
40 | if (mode === "test") {
41 | build = {
42 | rollupOptions: {
43 | input: { tests: resolve(__dirname, "tests/index.html") },
44 | },
45 | };
46 | server.port = 5100;
47 | } else {
48 | build = {
49 | rollupOptions: {
50 | input: { index: resolve(__dirname, "index.html") },
51 | },
52 | };
53 | }
54 |
55 | if (process.env.WX_BUILD_STATS) {
56 | build = {
57 | lib: {
58 | entry: resolve(__dirname, "src/index.js"),
59 | name,
60 | formats: ["es"],
61 | fileName: format => `${name}.${format}.js`,
62 | },
63 | outDir: "./dist",
64 | sourcemap: true,
65 | minify: true,
66 | target: "esnext",
67 | };
68 | publicDir = false;
69 | plugins.push(visualizer({ filename: "dist/stats.html" }));
70 | }
71 |
72 | if (files.length) await waitOn({ files });
73 |
74 | return {
75 | base,
76 | build,
77 | publicDir,
78 | resolve: { dedupe: ["svelte"] },
79 | plugins,
80 | server,
81 | watch: {
82 | persistent: true,
83 | include: ["src/**/*.ts", "src/**/*.js"],
84 | },
85 | };
86 | };
87 |
--------------------------------------------------------------------------------
/svelte/whatsnew.md:
--------------------------------------------------------------------------------
1 | ## 2.1.2
2 |
3 | ### Fixes
4 |
5 | - Editor en-US locale is not applied by default
6 |
7 | ## 2.1.1
8 |
9 | ### Fixes
10 |
11 | - Section bars have light styling in WillowDark skin
12 | - Section bars are not clickable for inline Editor
13 | - Content overflow for inline Editor
14 |
15 | ## 2.1.0
16 |
17 | ### New features
18 |
19 | - TextArea and Checkbox as built-in types
20 |
21 | ### Fixes
22 |
23 | - values are not changed until recreated
24 |
25 | ## 2.0.1
26 |
27 | - Public release
28 |
29 | ### New features
30 |
31 | - Svelte 5 support
32 | - Comments and TaskList sections
33 |
34 | ## 0.6.0
35 |
36 | ### New features
37 |
38 | - Validation
39 |
40 | ## 0.5.0
41 |
42 | ### New features
43 |
44 | - Ability to define collapsible sections
45 | - Ability to bind values to toolbar controls
46 |
47 | ### Updates
48 |
49 | - `section:left|right` changed to `column:left|right`
50 |
51 | ## 0.2.0
52 |
53 | - Initial version
54 |
--------------------------------------------------------------------------------