diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 00000000..e5b6d8d6 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,8 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works +with multi-package repos, or single-package repos to help you version and publish your code. You can +find the full documentation for it [in our repository](https://github.com/changesets/changesets) + +We have a quick list of common questions to get you started engaging with this project in +[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 00000000..59e5a027 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "access": "public", + "baseBranch": "master", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..38972655 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,13 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example + +# Ignore files for PNPM, NPM and YARN +pnpm-lock.yaml +package-lock.json +yarn.lock diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 00000000..d16a0fcb --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,31 @@ +module.exports = { + root: true, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:svelte/recommended', + 'prettier' + ], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + parserOptions: { + sourceType: 'module', + ecmaVersion: 2020, + extraFileExtensions: ['.svelte'] + }, + + env: { + browser: true, + es2017: true, + node: true + }, + overrides: [ + { + files: ['*.svelte'], + parser: 'svelte-eslint-parser', + parserOptions: { + parser: '@typescript-eslint/parser' + } + } + ] +}; diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 00000000..85e5352a --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,33 @@ +name: CI + +on: + push: + branches: + - '**' + - '!master' + pull_request: + branches: + - '**' + - '!master' + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: + - '16.16' + - '18.x' + steps: + - uses: actions/checkout@v2 + - uses: pnpm/action-setup@v2 + with: + version: 7 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: 'pnpm' + - name: Install dependencies + run: pnpm install --frozen-lockfile + - run: pnpm test && pnpm package diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 00000000..951d2323 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,46 @@ +name: CP + +on: + push: + branches: + - master + pull_request: + branches: + - master +concurrency: ${{ github.workflow }}-${{ github.ref }} + +permissions: {} #reset + +jobs: + release: + # IMPORTANT: prevent this action from running on forks + if: github.repository == 'Shinji13/Altron-Rich-Text' + permissions: + contents: write # to create release (changesets/action) + pull-requests: write # to create pull request (changesets/action) + name: Release + runs-on: ubuntu-latest + strategy: + matrix: + node-version: + - '16.16' + - '18.x' + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2 + with: + version: 7 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{matrix.node-version}} + cache: 'pnpm' + - run: pnpm install --frozen-lockfile + - name: Publish to npm + id: changesets + uses: changesets/action@v1 + with: + publish: pnpm run release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a626a965 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +.DS_Store +node_modules +/build +/dist +/.svelte-kit +/package +.env +.env.* +!.env.example +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + +.obsidian \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..b6f27f13 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..38972655 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,13 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example + +# Ignore files for PNPM, NPM and YARN +pnpm-lock.yaml +package-lock.json +yarn.lock diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..a77fddea --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "useTabs": true, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "plugins": ["prettier-plugin-svelte"], + "pluginSearchDirs": ["."], + "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..f7997d35 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,27 @@ +# altron-rich-text + +## 0.0.4 + +### Patch Changes + +- 217ad5c: updating the readme , adding two props to customize block gap and margin-block +- Updated dependencies [217ad5c] + - altron-rich-text@0.0.4 + +## 0.0.3 + +### Patch Changes + +- d38082b: updating the readme + +## 0.0.2 + +### Patch Changes + +- 7baad92: removing the ondestroy method and adding a return function from onMount because it execute in the server + +## 0.0.1 + +### Patch Changes + +- 7baad92: adding if browser condition when executing onMount and onDestroy to prevent window is not defined diff --git a/README.md b/README.md new file mode 100644 index 00000000..4525454b --- /dev/null +++ b/README.md @@ -0,0 +1,231 @@ + +# Altron Rich Text Svelte Component + +![altronLogo](./static/altronGreen.jpg) + +## Introduction + +The **Altron Rich Text Svelte Component** is a powerful and versatile rich text editor for Svelte applications. It allows users to create, edit, and manage structured text content by adding various blocks with associated data. This component is designed to be highly customizable, responsive on mobile devices, and supportive of both editing and viewing modes. + +## Installation + +To get started with the Altron Rich Text Svelte Component, you can install it via your package manager of choice: + +```bash +npm install altron-rich-text +# or +yarn add altron-rich-text +# or +pnpm i altron-rich-text +``` + +## Basic Usage + +To use the Altron Rich Text editor, import the `AltronRichText` component and include it in your Svelte application. + +```svelte + + + +``` + +## Data Structure + +The rich text editor works with a specific data structure known as `dataBlock`. This type includes: + +- `image`: Represents image blocks with data such as base64 representation, name, and caption. +- `paragraph`: Basic text blocks. +- `code`: Code blocks with text content and a specified programming language. +- `quote`: Text quotes with owner attribution. +- `header`: Header blocks with different levels (1 to 4) and associated text. +- `space`: Empty space blocks with a specified size. +- `list`: List blocks with items and an ordered or unordered list type. + +```ts +type dataBlock = +| { name: 'image'; id: string; data: { base64: string; name: string; caption: string } } +| { name: 'paragraph'; id: string; data: { text: string } } +| { name: 'code'; id: string; data: { text: string; lang: languages } } +| { name: 'quote'; id: string; data: { text: string; owner: string } } +| { name: 'header'; id: string; data: { text: string; level: 1 | 2 | 3 | 4 } } +| { name: 'space'; id: string; data: { size: number } } +| { name: 'list'; id: string; data: { items: string[]; type: 'ordered' | 'unordered' } }; +``` + +### Block States + +Each block in the Altron Rich Text editor can exist in one of three states: + +1. **View State:** In this state, the block displays its information based on its type and associated data. +2. **Focus State:** When a user clicks on a block, it switches to the focus state. In this state, the block is wrapped with a wrapper that provides options for deleting the block and reordering it (moving it up or down). +3. **Edit State:** Upon another click, the block enters the edit state, allowing the user to modify the block's information. ## Customization + +## Customization + +You can customize various aspects of the rich text editor: + +- **Colors:** + - `primaryColor`: Used in both focus and view states. + - `secondaryColor`: Specifically used in the edit state. + - `textColor`: Defines the text color within the editor. + - `bgColor`: Specifies the background color of the editor. + +- **Fonts:** + - `headerFont`: Set the font for header elements (e.g., h1, h2, h3, h4). + - `bodyFont`: Define the font for general text elements (e.g., p, span, label, li, a). + +- **Font Sizes and Line Heights:** + - Customize font sizes using `h1`, `h2`, `h3`, `h4`, `body`, `small`. + - Set line heights for various text elements using `lh1`, `lh2`, `lh3`, `lh4`, and `lbody`. + +- **Custom Code Themes:** Users can import and apply custom themes from `svelte-highlight` for code highlighting by setting the `codeTheme` prop. + +- **Initial Data:** You can prepopulate the editor with initial data by passing an array of `dataBlock` to the `initialData` prop. + +- **Custom Code Block Languages:** Define the list of languages users can use for code blocks with the `codeBlockLanguages` prop. By default, it includes JavaScript, Java, C, CSS, TypeScript, Python, and C#. + +- **Custom spacing:** By default **altron** separate blocks with 10px gap and have **margin-block** set to 30px you can change that using **blocksGap** and **marginBlock** props . + +- **Custom Components:** You can replace the default view components for various block types with your custom components. For example: + - `customImage` for image blocks + - `customCode` for code blocks + - `customList` for list blocks + - `customHeader` for header blocks + - `customParagraph` for paragraph blocks + - `customQuote` for quote blocks + +```ts +export let customImage: ComponentType< +SvelteComponent<{ base64: string; name: string; caption: string }> +> = ViewImage; + +export let customCode: ComponentType> = +ViewCode; + +export let customList: ComponentType< +SvelteComponent<{ items: string[]; type: 'ordered' | 'unordered' }> +> = ViewList; + +export let customHeader: ComponentType> = +ViewHeader; + +export let customParagraph: ComponentType> = ViewParagraph; + +export let customQuote: ComponentType> = +ViewQuote; +``` + +## View Mode + +The Altron Rich Text editor includes a `viewMode` prop, which, when set to `true`, allows you to use the editor in a read-only mode. In this mode, you can display existing content without enabling editing. + +```svelte + + + +``` + +## The getData Function + +The package provides a function to retrieve the `dataBlock` at any given moment. Here is an example: + +```ts + + + + +``` + +## Svelte Highlight + +The package uses `svelte-highlight` with `autoHighlight` functionality for code highlighting. This feature enhances the visual representation of code blocks in your rich text editor. However, note that enabling `autoHighlight` may result in a larger bundle size. You may need to consider using customCode component. + +## Props + +Here are all AltronRichText props and their default values: + +```ts +export let initialData: dataBlock[] = []; + +export let viewMode = false; + +export let blocksGap = 10; + +export let marginBlock = 30; + +export let headerFont = `Verdana, sans-serif`; + +export let bodyFont = `Helvetica, sans-serif`; + +export let primaryColor = '#3366FF'; + +export let secondaryColor = '#1eeb36'; + +export let textColor = '#121212'; + +export let bgColor = '#ffffff'; + +export let h1 = 'clamp(1.8rem, calc(1.8rem + ((1vw - 0.48rem) * 0.9722)), 2.1rem)'; + +export let h2 = 'clamp(1.5rem, calc(1.5rem + ((1vw - 0.48rem) * 0.9722)), 1.8rem)'; + +export let h3 = 'clamp(1.2rem, calc(1.2rem + ((1vw - 0.48rem) * 0.9722)), 1.5rem)'; + +export let h4 = 'clamp(1.125rem, calc(1.15rem + ((1vw - 0.48rem) * 0.3472)), 1.2rem)'; + +export let body = 'clamp(1rem, calc(1rem + ((1vw - 0.48rem) * 0.1736)), 1.125rem)'; + +export let small = 'clamp(0.875rem, calc(0.875rem + ((1vw - 0.48rem) * 0.1736)), 1rem)'; + +export let lh1 = '1.3'; + +export let lh2 = '1.35'; + +export let lh3 = '1.4'; + +export let lh4 = '1.5'; + +export let lbody = '1.6'; + +export let codeTheme: string = nightOwl; + +export let customImage: ComponentType< +SvelteComponent<{ base64: string; name: string; caption: string }> +> = ViewImage; + +export let customCode: ComponentType> = +ViewCode; + +export let customList: ComponentType< +SvelteComponent<{ items: string[]; type: 'ordered' | 'unordered' }> +> = ViewList; + +export let customHeader: ComponentType> = +ViewHeader; + +export let customParagraph: ComponentType> = ViewParagraph; + +export let customQuote: ComponentType> = +ViewQuote; + +export let codeBlockLanguages: languages[] = [ +'javascript', +'java', +'c', +'css', +'typescript', +'python', +'csharp' +]; +``` diff --git a/package.json b/package.json new file mode 100644 index 00000000..aba033e2 --- /dev/null +++ b/package.json @@ -0,0 +1,89 @@ +{ + "name": "@valiantlynx/svelte-rich-text", + "version": "0.0.1", + "description": "Rich text editor as a svelte component", + "maintainers": [ + "valiantlynx (https://www.valiantlynx.com/)" + ], + "engines": { + "node": ">=16.14" + }, + "keywords": [ + "sveltekit", + "rich text editor", + "javascript", + "typescript", + "svelte", + "blocks" + ], + "author": "valiantlynx", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/valiantlynx/svelte-rich-text" + }, + "scripts": { + "dev": "vite --host", + "build": "vite build", + "preview": "vite preview", + "package": "svelte-kit sync && svelte-package && publint", + "release": "pnpm run package && npx changeset publish", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "test": "vitest", + "lint": "prettier --plugin-search-dir . --check . && eslint .", + "format": "prettier --plugin-search-dir . --write ." + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "svelte": "./dist/index.js" + } + }, + "files": [ + "dist", + "!dist/**/*.test.*", + "!dist/**/*.spec.*" + ], + "peerDependencies": { + "svelte": "^4.0.0" + }, + "devDependencies": { + "@changesets/cli": "^2.26.2", + "@sveltejs/adapter-auto": "^2.0.0", + "@sveltejs/kit": "^1.20.4", + "@sveltejs/package": "^2.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "autoprefixer": "^10.4.14", + "eslint": "^8.28.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-svelte": "^2.30.0", + "postcss-load-config": "^4.0.1", + "prettier": "^2.8.0", + "prettier-plugin-svelte": "^2.10.1", + "publint": "^0.1.9", + "svelte": "^4.0.5", + "svelte-check": "^3.4.3", + "tslib": "^2.4.1", + "typescript": "^5.0.0", + "vite": "^4.4.2", + "vitest": "^0.34.0" + }, + "svelte": "./dist/index.js", + "types": "./dist/index.d.ts", + "type": "module", + "typesVersions": { + ">4.0": { + ".": [ + "./dist/index.d.ts" + ] + } + }, + "dependencies": { + "altron-rich-text": "^0.0.4", + "short-uuid": "^4.2.2", + "svelte-highlight": "^7.4.1", + "svelte-select": "^5.7.0" + } +} diff --git a/src/app.d.ts b/src/app.d.ts new file mode 100644 index 00000000..f59b884c --- /dev/null +++ b/src/app.d.ts @@ -0,0 +1,12 @@ +// See https://kit.svelte.dev/docs/types#app +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface Platform {} + } +} + +export {}; diff --git a/src/app.html b/src/app.html new file mode 100644 index 00000000..d2fc6b06 --- /dev/null +++ b/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/src/lib/assets/default.jpg b/src/lib/assets/default.jpg new file mode 100644 index 00000000..dc3ba221 Binary files /dev/null and b/src/lib/assets/default.jpg differ diff --git a/src/lib/components/core/blockWrapper.svelte b/src/lib/components/core/blockWrapper.svelte new file mode 100644 index 00000000..0eb7ad74 --- /dev/null +++ b/src/lib/components/core/blockWrapper.svelte @@ -0,0 +1,91 @@ + + +
+ {#if dataBlock.name == 'header'} +
+ {:else if dataBlock.name == 'code'} + + {:else if dataBlock.name == 'image'} + + {:else if dataBlock.name == 'list'} + + {:else if dataBlock.name == 'paragraph'} + + {:else if dataBlock.name == 'quote'} + + {:else} + + {/if} + {dataBlock.name} +
+ + diff --git a/src/lib/components/core/editMode.svelte b/src/lib/components/core/editMode.svelte new file mode 100644 index 00000000..ae51a4d9 --- /dev/null +++ b/src/lib/components/core/editMode.svelte @@ -0,0 +1,120 @@ + + +{#each $data as block} +
+ + {#if $workingBlock?.id == block.id && $workingBlock.state == 'focused'} + + {/if} +
+{/each} + + + + diff --git a/src/lib/components/core/main.svelte b/src/lib/components/core/main.svelte new file mode 100644 index 00000000..3c0f8187 --- /dev/null +++ b/src/lib/components/core/main.svelte @@ -0,0 +1,151 @@ + + +
+ {#if viewMode} + + {:else} + + {/if} +
+ + diff --git a/src/lib/components/core/toolBar.svelte b/src/lib/components/core/toolBar.svelte new file mode 100644 index 00000000..f4e073c9 --- /dev/null +++ b/src/lib/components/core/toolBar.svelte @@ -0,0 +1,145 @@ + + +
+ + + + (toggle = !toggle)} class="control"> + {#if toggle} + + {:else} + + {/if} + + {#if toggle} +
+ {#each options.entries() as option, index} + + + { + const id = shortUUID().generate(); + data.update((prev) => { + add(prev, id, option[0]); + toggle = true; + return prev; + }); + workingBlock.set({ id, state: 'editing' }); + }} + > + + + {/each} +
+ {/if} +
+ + diff --git a/src/lib/components/core/viewMode.svelte b/src/lib/components/core/viewMode.svelte new file mode 100644 index 00000000..b09f4cf2 --- /dev/null +++ b/src/lib/components/core/viewMode.svelte @@ -0,0 +1,28 @@ + + +{#each $data as block} + {#if block.name == 'code'} + + {:else if block.name == 'header'} + + {:else if block.name == 'list'} + + {:else if block.name == 'image'} + + {:else if block.name == 'quote'} + + {:else if block.name == 'paragraph'} + + {:else} + + {/if} +{/each} diff --git a/src/lib/components/editBlocks/code.svelte b/src/lib/components/editBlocks/code.svelte new file mode 100644 index 00000000..16d77e04 --- /dev/null +++ b/src/lib/components/editBlocks/code.svelte @@ -0,0 +1,72 @@ + + +{#if active} +
+