Obsidian review and eslint fixes, readme update (#2)

* updated installation instruction

* fixed warnings from automatic review

* removed unused dependencies

* updated eslint settings and fixed warnings

* fixed esling warnings, moved screenshot to .github

* bumped version
This commit is contained in:
Poluboiarinov Anton 2026-05-16 07:54:55 +03:00 committed by GitHub
parent 1f49b8583f
commit ede78786f7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 209 additions and 191 deletions

View file

@ -1,3 +0,0 @@
node_modules/
main.js

View file

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 152 KiB

View file

@ -4,10 +4,7 @@ No need to move tasks around manually — the automatic layout keeps your struct
## How to install
*Taskmap is in beta. Installation from Obsidian community plugins is currently unavailable.*
Install [BRAT](https://github.com/TfTHacker/obsidian42-brat) community plugin from obsidian app.
Go to settings, press `Add beta plugin` and paste link to this repo https://github.com/poanse/obsidian-taskmap
Taskmap is available via obsidian community portal https://community.obsidian.md/plugins/taskmap and "Community plugins" option in the obsidian app.
## Why Taskmap?
@ -29,4 +26,4 @@ Canvas-style visualization, zero manual positioning.
- Automatic parent status from children
- Link tasks outside of tree structure using blocker system
![screenshot.png](screenshot.png)
![screenshot.png](.github/screenshot.png)

View file

@ -1,6 +1,5 @@
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
import esbuildSvelte from "esbuild-svelte";
import { sveltePreprocess } from "svelte-preprocess";
import fs from "fs";
@ -54,7 +53,6 @@ const context = await esbuild.context({
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins,
],
format: "cjs",
target: "es2022",

View file

@ -1,31 +1,79 @@
// eslint.config.mjs
import ts from "typescript-eslint";
import js from "@eslint/js";
import tsparser from "@typescript-eslint/parser";
import { defineConfig } from "eslint/config";
import obsidianmd from "eslint-plugin-obsidianmd";
import svelte from "eslint-plugin-svelte";
import svelteParser from "svelte-eslint-parser";
import prettier from "eslint-config-prettier";
import js from "@eslint/js";
import ts from "typescript-eslint";
import globals from "globals";
export default defineConfig([
...obsidianmd.configs.recommended,
{
ignores: [
"node_modules/**",
"dist/**",
"main.js",
"package.json",
"package-lock.json",
"versions.json",
"eslint.config.*",
"esbuild.config.mjs",
],
},
js.configs.recommended,
...ts.configs.recommended,
...obsidianmd.configs.recommended,
...svelte.configs["flat/recommended"],
...svelte.configs["flat/prettier"],
prettier,
{
files: ["**/*.ts", "**/*.svelte"],
languageOptions: {
parser: ts.parser,
parserOptions: { project: "./tsconfig.json" },
},
// You can add your own configuration to override or add rules
rules: {
// example: turn off a rule from the recommended set
// "obsidianmd/sample-names": "off",
// example: add a rule not in the recommended set and set its severity
// "obsidianmd/prefer-file-manager-trash": "error",
{
files: ["**/*.ts"],
extends: [...ts.configs.recommendedTypeChecked],
languageOptions: {
parser: tsparser,
parserOptions: {
project: "./tsconfig.json",
tsconfigRootDir: import.meta.dirname,
},
},
},
{
files: ["src/**/*.svelte"],
languageOptions: {
parser: svelteParser,
parserOptions: {
parser: tsparser,
extraFileExtensions: [".svelte"],
},
globals: {
...globals.browser,
},
},
rules: {
"@typescript-eslint/no-deprecated": "off",
},
},
{
files: ["scripts/**/*.ts"],
rules: {
"import/no-nodejs-modules": "off",
},
},
{
files: ["scripts/**/*.ts", "*.mjs"],
languageOptions: {
globals: {
...globals.node,
},
},
rules: {
"import/no-nodejs-modules": "off",
},
},
prettier,
]);

View file

@ -1,7 +1,7 @@
{
"id": "taskmap",
"name": "Taskmap",
"version": "0.1.5",
"version": "0.1.6",
"minAppVersion": "1.12.4",
"description": "Plan projects via interactive GUI task trees with automatic layout.",
"author": "poanse",

104
package-lock.json generated
View file

@ -1,18 +1,16 @@
{
"name": "obsidian-taskmap",
"version": "0.1.5",
"version": "0.1.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "obsidian-taskmap",
"version": "0.1.3",
"version": "0.1.5",
"license": "MIT",
"dependencies": {
"@napolab/alpha-blend": "^2.0.0",
"@panzoom/panzoom": "^4.6.1",
"autobind-decorator": "^2.4.0",
"esm-env": "^1.2.2",
"lucide-svelte": "^0.562.0",
"rollup-plugin-css-only": "^4.5.5",
"ts-pattern": "^5.9.0",
@ -24,10 +22,9 @@
"@types/node": "latest",
"@typescript-eslint/eslint-plugin": "latest",
"@typescript-eslint/parser": "latest",
"builtin-modules": "latest",
"esbuild": "latest",
"esbuild-svelte": "^0.9.3",
"eslint": "^9.39.2",
"eslint": "^9.39.4",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-obsidianmd": "^0.1.9",
"eslint-plugin-svelte": "^3.14.0",
@ -541,15 +538,15 @@
}
},
"node_modules/@eslint/config-array": {
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
"integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
"version": "0.21.2",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
"integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@eslint/object-schema": "^2.1.7",
"debug": "^4.3.1",
"minimatch": "^3.1.2"
"minimatch": "^3.1.5"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -635,20 +632,20 @@
}
},
"node_modules/@eslint/eslintrc": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
"integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
"integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
"dev": true,
"license": "MIT",
"dependencies": {
"ajv": "^6.12.4",
"ajv": "^6.14.0",
"debug": "^4.3.2",
"espree": "^10.0.1",
"globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.1",
"minimatch": "^3.1.2",
"minimatch": "^3.1.5",
"strip-json-comments": "^3.1.1"
},
"engines": {
@ -737,9 +734,9 @@
}
},
"node_modules/@eslint/js": {
"version": "9.39.2",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
"integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
"version": "9.39.4",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
"integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
"dev": true,
"license": "MIT",
"peer": true,
@ -1628,9 +1625,9 @@
}
},
"node_modules/ajv": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
"integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1846,16 +1843,6 @@
"node": ">= 0.4"
}
},
"node_modules/autobind-decorator": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/autobind-decorator/-/autobind-decorator-2.4.0.tgz",
"integrity": "sha512-OGYhWUO72V6DafbF8PM8rm3EPbfuyMZcJhtm5/n26IDwO18pohE4eNazLoCGhPiXOCD0gEGmrbU3849QvM8bbw==",
"license": "MIT",
"engines": {
"node": ">=8.10",
"npm": ">=6.4.1"
}
},
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@ -1898,19 +1885,6 @@
"balanced-match": "^1.0.0"
}
},
"node_modules/builtin-modules": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz",
"integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18.20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
@ -2215,9 +2189,9 @@
}
},
"node_modules/devalue": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.7.1.tgz",
"integrity": "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA==",
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz",
"integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==",
"license": "MIT"
},
"node_modules/dunder-proto": {
@ -2510,26 +2484,26 @@
}
},
"node_modules/eslint": {
"version": "9.39.2",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"version": "9.39.4",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
"integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.21.1",
"@eslint/config-array": "^0.21.2",
"@eslint/config-helpers": "^0.4.2",
"@eslint/core": "^0.17.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.39.2",
"@eslint/eslintrc": "^3.3.5",
"@eslint/js": "9.39.4",
"@eslint/plugin-kit": "^0.4.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6",
"ajv": "^6.12.4",
"ajv": "^6.14.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
@ -2548,7 +2522,7 @@
"is-glob": "^4.0.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"minimatch": "^3.1.5",
"natural-compare": "^1.4.0",
"optionator": "^0.9.3"
},
@ -3377,9 +3351,9 @@
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
"integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
"dev": true,
"funding": [
{
@ -4848,9 +4822,9 @@
}
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"version": "8.5.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
"integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
"dev": true,
"funding": [
{
@ -5720,9 +5694,9 @@
}
},
"node_modules/svelte": {
"version": "5.55.3",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.3.tgz",
"integrity": "sha512-dS1N+i3bA1v+c4UDb750MlN5vCO82G6vxh8HeTsPsTdJ1BLsN1zxSyDlIdBBqUjqZ/BxEwM8UrFf98aaoVnZFQ==",
"version": "5.55.7",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.7.tgz",
"integrity": "sha512-ymI5ykLPwIHW839E053FQbI1G+jnRFJEw3Kv5Y4njixVWywQBx+NUFpkkKyk5LIb36Fg9DVXSYpqiGekLD0hyw==",
"license": "MIT",
"peer": true,
"dependencies": {
@ -5735,7 +5709,7 @@
"aria-query": "5.3.1",
"axobject-query": "^4.1.0",
"clsx": "^2.1.1",
"devalue": "^5.6.4",
"devalue": "^5.8.1",
"esm-env": "^1.2.1",
"esrap": "^2.2.4",
"is-reference": "^3.0.3",

View file

@ -1,6 +1,6 @@
{
"name": "obsidian-taskmap",
"version": "0.1.5",
"version": "0.1.6",
"description": "Taskmap plugin for Obsidian (https://obsidian.md)",
"main": "main.js",
"scripts": {
@ -8,7 +8,9 @@
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json",
"svelte-check": "svelte-check --tsconfig tsconfig.json",
"perf:tasks": "tsx scripts/perf-tasks.ts"
"perf:tasks": "tsx scripts/perf-tasks.ts",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"keywords": [],
"author": "",
@ -19,10 +21,9 @@
"@types/node": "latest",
"@typescript-eslint/eslint-plugin": "latest",
"@typescript-eslint/parser": "latest",
"builtin-modules": "latest",
"esbuild": "latest",
"esbuild-svelte": "^0.9.3",
"eslint": "^9.39.2",
"eslint": "^9.39.4",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-obsidianmd": "^0.1.9",
"eslint-plugin-svelte": "^3.14.0",
@ -41,8 +42,6 @@
"dependencies": {
"@napolab/alpha-blend": "^2.0.0",
"@panzoom/panzoom": "^4.6.1",
"autobind-decorator": "^2.4.0",
"esm-env": "^1.2.2",
"lucide-svelte": "^0.562.0",
"rollup-plugin-css-only": "^4.5.5",
"ts-pattern": "^5.9.0",

View file

@ -180,7 +180,7 @@ export class Context {
public finishTaskDragging = (e: PointerEvent, updatePositions = false) => {
this.draggedTaskId = NoTaskId;
this.taskDraggingManager.onPointerUp(e);
this.taskDraggingManager.onPointerUp();
if (updatePositions) {
this.updateTaskPositions();
}
@ -315,7 +315,7 @@ export class Context {
const taskIdSet: ReadonlySet<TaskId> = new SvelteSet<TaskId>(
this.taskPositions.map((t) => t.taskId),
);
this.positions.forEach((v, k) => {
this.positions.forEach((_v, k) => {
if (k != NoTaskId && !taskIdSet.has(k)) {
this.taskPositions.push({
taskId: k,
@ -426,8 +426,11 @@ export class Context {
this.app.workspace
.getLeavesOfType(TASKMAP_VIEW_TYPE)
.map((leaf) => leaf.view)
.filter((view) => view instanceof TaskmapView)
.forEach((view) => view.debouncedSave());
.forEach((view) => {
if (view instanceof TaskmapView) {
view.debouncedSave();
}
});
}
public addTask(parentId: TaskId): void {

View file

@ -27,7 +27,7 @@ export class DraggingManager {
}
};
public onPointerUp = (e: PointerEvent) => {
public onPointerUp = () => {
console.debug(`DraggingManager pointerUp: ${this.deltaX}`);
this.reset();
};

View file

@ -321,7 +321,7 @@ export class NodePositionsCalculator {
rootChildrenByPriority.set(idx, id);
});
rootChildrenByPriority.forEach((priority, chId) => {
rootChildrenByPriority.forEach((_priority, chId) => {
const x = taskById.get(chId)!;
if (this.RowIndex(x) === 0) {
const neighbourId =

View file

@ -8,7 +8,6 @@
let { iconCode, context }: { iconCode: SettingsIconCode, context: Context } = $props();
let isPressedDown = $state(false);
let modal: ProjectSettingsModal | null = null;
function onpointerdown(event: MouseEvent) {
isPressedDown = true;
@ -84,8 +83,8 @@
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0px;
padding: 0px;
gap: 0;
padding: 0;
/*background-color: #e0e0e0;*/
cursor: pointer;
border-radius: 4px;

View file

@ -132,7 +132,7 @@
viewportEl!.focus();
e.stopPropagation();
}
draggingManager.onPointerUp(e);
draggingManager.onPointerUp();
context.finishTaskDragging(e, true);
}
@ -220,7 +220,7 @@
const blockerT = context.versionedData.getTask(p.blocker);
return blockedT.status !== StatusCode.DONE && blockerT.status !== StatusCode.DONE && !blockerT.deleted && !blockedT.deleted;
}
)) as pair}
)) as pair (pair.blocker + '-' + pair.blocked)}
<Connection
startTaskId={pair.blocker}
endTaskId={pair.blocked}

View file

@ -105,7 +105,7 @@
"
>
{#key context.updateOnZoomCounter}
{#each toolbarButtons as button}
{#each toolbarButtons as button (button)}
<Button iconCode={button} {context} />
{/each}
{/key}
@ -146,7 +146,7 @@
left: {subtoolbarLeftShift(toolbarButtons.indexOf(IconCode.STATUS_SUBMENU))}px;
"
>
{#each statusButtons as button}
{#each statusButtons as button (button)}
<Button
iconCode={button}
{context}

View file

@ -33,7 +33,7 @@ export class FolderSuggest extends AbstractInputSuggest<TFolder> {
}
renderSuggestion(file: TFolder, el: HTMLElement) {
el.createEl("div", { text: file.path });
el.createDiv({ text: file.path });
el.createEl("small", { text: file.path, cls: "text-muted" });
}

View file

@ -45,7 +45,7 @@ export class LinkSuggest extends AbstractInputSuggest<TFile> {
}
renderSuggestion(file: TFile, el: HTMLElement) {
el.createEl("div", { text: file.basename });
el.createDiv({ text: file.basename });
el.createEl("small", { text: file.path, cls: "text-muted" });
}

View file

@ -1,76 +0,0 @@
import { match } from "ts-pattern";
/**
* Slides an element in and out.
*
* @param {Element} node
* @param {SlideParamsCustom} [params]
* @returns {TransitionConfig}
*/
export function slideCustom(
node,
{ delay = 0, duration = 400, easing = "cubic_out", axis = "y" } = {},
) {
const style = getComputedStyle(node);
const opacity = +style.opacity;
const primary_property = axis === "y" || axis === "-y" ? "height" : "width";
const primary_property_value = parseFloat(style[primary_property]);
const secondary_properties = match(axis)
.with("y", () => ["top", "bottom"])
.with("-y", () => ["top", "bottom"])
.with("x", () => ["left", "right"])
.with("-x", () => ["left", "right"])
.otherwise((x) => {
throw new Error();
});
const capitalized_secondary_properties = secondary_properties.map(
(e) =>
/** @type {'Left' | 'Right' | 'Top' | 'Bottom'} */ `${e[0].toUpperCase()}${e.slice(1)}`,
);
const padding_start_value = parseFloat(
style[`padding${capitalized_secondary_properties[0]}`],
);
const padding_end_value = parseFloat(
style[`padding${capitalized_secondary_properties[1]}`],
);
const margin_start_value = parseFloat(
style[`margin${capitalized_secondary_properties[0]}`],
);
const margin_end_value = parseFloat(
style[`margin${capitalized_secondary_properties[1]}`],
);
const border_width_start_value = parseFloat(
style[`border${capitalized_secondary_properties[0]}Width`],
);
const border_width_end_value = parseFloat(
style[`border${capitalized_secondary_properties[1]}Width`],
);
let additionalString = "";
if (axis === "-y") {
}
return {
delay,
duration,
easing,
css: (t) =>
"overflow: hidden;" +
`opacity: ${Math.min(t * 20, 1) * opacity};` +
`${primary_property}: ${t * primary_property_value}px;` +
`padding-${secondary_properties[0]}: ${t * padding_start_value}px;` +
`padding-${secondary_properties[1]}: ${t * padding_end_value}px;` +
`margin-${secondary_properties[0]}: ${t * margin_start_value}px;` +
`margin-${secondary_properties[1]}: ${t * margin_end_value}px;` +
// `border-${secondary_properties[0]}-width: ${t * border_width_start_value}px;` +
// `border-${secondary_properties[1]}-width: ${t * border_width_end_value}px;` +
`min-${primary_property}: 0` +
(axis === "-y"
? `; top: ${parseFloat(style["top"]) + primary_property_value * (1 - t)}px`
: "") +
(axis === "-x"
? `; left: ${parseFloat(style["left"]) + primary_property_value * (1 - t)}px`
: ""),
};
}

79
src/helpers/svelte.ts Normal file
View file

@ -0,0 +1,79 @@
import { match } from "ts-pattern";
import type { TransitionConfig } from "svelte/transition";
type Axis = "x" | "-x" | "y" | "-y";
type SecondaryProperty = "top" | "bottom" | "left" | "right";
type CapitalizedSecondaryProperty = "Top" | "Bottom" | "Left" | "Right";
export interface SlideParamsCustom {
delay?: number;
duration?: number;
easing?: TransitionConfig["easing"];
axis?: Axis;
}
/**
* Slides an element in and out.
*/
export function slideCustom(
node: Element,
{ delay = 0, duration = 400, easing, axis = "y" }: SlideParamsCustom = {},
): TransitionConfig {
const style = getComputedStyle(node);
const opacity = Number(style.opacity);
const primaryProperty: "height" | "width" =
axis === "y" || axis === "-y" ? "height" : "width";
const primaryPropertyValue = parseFloat(style[primaryProperty]);
const secondaryProperties = match<
Axis,
[SecondaryProperty, SecondaryProperty]
>(axis)
.with("y", () => ["top", "bottom"])
.with("-y", () => ["top", "bottom"])
.with("x", () => ["left", "right"])
.with("-x", () => ["left", "right"])
.exhaustive();
const capitalizedSecondaryProperties = secondaryProperties.map(
(property): CapitalizedSecondaryProperty =>
`${property[0].toUpperCase()}${property.slice(1)}` as CapitalizedSecondaryProperty,
);
const paddingStartValue = parseFloat(
style[`padding${capitalizedSecondaryProperties[0]}`],
);
const paddingEndValue = parseFloat(
style[`padding${capitalizedSecondaryProperties[1]}`],
);
const marginStartValue = parseFloat(
style[`margin${capitalizedSecondaryProperties[0]}`],
);
const marginEndValue = parseFloat(
style[`margin${capitalizedSecondaryProperties[1]}`],
);
return {
delay,
duration,
easing,
css: (t: number) =>
"overflow: hidden;" +
`opacity: ${Math.min(t * 20, 1) * opacity};` +
`${primaryProperty}: ${t * primaryPropertyValue}px;` +
`padding-${secondaryProperties[0]}: ${t * paddingStartValue}px;` +
`padding-${secondaryProperties[1]}: ${t * paddingEndValue}px;` +
`margin-${secondaryProperties[0]}: ${t * marginStartValue}px;` +
`margin-${secondaryProperties[1]}: ${t * marginEndValue}px;` +
`min-${primaryProperty}: 0` +
(axis === "-y"
? `; top: ${parseFloat(style.top) + primaryPropertyValue * (1 - t)}px`
: "") +
(axis === "-x"
? `; left: ${parseFloat(style.left) + primaryPropertyValue * (1 - t)}px`
: ""),
};
}