generated from tpl/obsidian-sample-plugin
			Rewrite bookshelf view from scratch
This commit is contained in:
		
							parent
							
								
									2df1cf4b30
								
							
						
					
					
						commit
						349fcd903e
					
				| 
						 | 
					@ -111,6 +111,7 @@ const context = await esbuild.context({
 | 
				
			||||||
		"@lezer/lr",
 | 
							"@lezer/lr",
 | 
				
			||||||
		...builtins,
 | 
							...builtins,
 | 
				
			||||||
	],
 | 
						],
 | 
				
			||||||
 | 
						conditions: ["svelte"],
 | 
				
			||||||
	format: "cjs",
 | 
						format: "cjs",
 | 
				
			||||||
	target: "es2018",
 | 
						target: "es2018",
 | 
				
			||||||
	logLevel: "info",
 | 
						logLevel: "info",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,9 +44,13 @@
 | 
				
			||||||
		"typescript": "5.0.4"
 | 
							"typescript": "5.0.4"
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	"dependencies": {
 | 
						"dependencies": {
 | 
				
			||||||
 | 
							"@humanspeak/svelte-virtual-list": "^0.2.6",
 | 
				
			||||||
 | 
							"@leveluptuts/svelte-fit": "^1.0.3",
 | 
				
			||||||
		"chart.js": "^4.5.0",
 | 
							"chart.js": "^4.5.0",
 | 
				
			||||||
		"chroma-js": "^3.1.2",
 | 
							"chroma-js": "^3.1.2",
 | 
				
			||||||
		"esbuild-sass-plugin": "^3.3.1",
 | 
							"esbuild-sass-plugin": "^3.3.1",
 | 
				
			||||||
 | 
							"fitty": "^2.4.2",
 | 
				
			||||||
 | 
							"just-memoize": "^2.2.0",
 | 
				
			||||||
		"textfit": "^2.4.0",
 | 
							"textfit": "^2.4.0",
 | 
				
			||||||
		"uuid": "^11.1.0",
 | 
							"uuid": "^11.1.0",
 | 
				
			||||||
		"yaml": "^2.8.0",
 | 
							"yaml": "^2.8.0",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,12 @@ importers:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .:
 | 
					  .:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@humanspeak/svelte-virtual-list':
 | 
				
			||||||
 | 
					        specifier: ^0.2.6
 | 
				
			||||||
 | 
					        version: 0.2.6(svelte@5.34.8)
 | 
				
			||||||
 | 
					      '@leveluptuts/svelte-fit':
 | 
				
			||||||
 | 
					        specifier: ^1.0.3
 | 
				
			||||||
 | 
					        version: 1.0.3
 | 
				
			||||||
      chart.js:
 | 
					      chart.js:
 | 
				
			||||||
        specifier: ^4.5.0
 | 
					        specifier: ^4.5.0
 | 
				
			||||||
        version: 4.5.0
 | 
					        version: 4.5.0
 | 
				
			||||||
| 
						 | 
					@ -17,6 +23,12 @@ importers:
 | 
				
			||||||
      esbuild-sass-plugin:
 | 
					      esbuild-sass-plugin:
 | 
				
			||||||
        specifier: ^3.3.1
 | 
					        specifier: ^3.3.1
 | 
				
			||||||
        version: 3.3.1(esbuild@0.17.3)(sass-embedded@1.89.2)
 | 
					        version: 3.3.1(esbuild@0.17.3)(sass-embedded@1.89.2)
 | 
				
			||||||
 | 
					      fitty:
 | 
				
			||||||
 | 
					        specifier: ^2.4.2
 | 
				
			||||||
 | 
					        version: 2.4.2
 | 
				
			||||||
 | 
					      just-memoize:
 | 
				
			||||||
 | 
					        specifier: ^2.2.0
 | 
				
			||||||
 | 
					        version: 2.2.0
 | 
				
			||||||
      textfit:
 | 
					      textfit:
 | 
				
			||||||
        specifier: ^2.4.0
 | 
					        specifier: ^2.4.0
 | 
				
			||||||
        version: 2.4.0
 | 
					        version: 2.4.0
 | 
				
			||||||
| 
						 | 
					@ -312,6 +324,11 @@ packages:
 | 
				
			||||||
    resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
 | 
					    resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
 | 
				
			||||||
    engines: {node: '>=18.18.0'}
 | 
					    engines: {node: '>=18.18.0'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@humanspeak/svelte-virtual-list@0.2.6':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-nfD81b4LQw2bTSFYV/M0ky/pnUkfI0KYr5qCVJHZe6h3dUtXoNIhoxypI6JGBcpW3D8jN5Y53NOkfDggJNb5nA==}
 | 
				
			||||||
 | 
					    peerDependencies:
 | 
				
			||||||
 | 
					      svelte: ^5.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@humanwhocodes/module-importer@1.0.1':
 | 
					  '@humanwhocodes/module-importer@1.0.1':
 | 
				
			||||||
    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
 | 
					    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
 | 
				
			||||||
    engines: {node: '>=12.22'}
 | 
					    engines: {node: '>=12.22'}
 | 
				
			||||||
| 
						 | 
					@ -345,6 +362,9 @@ packages:
 | 
				
			||||||
  '@kurkle/color@0.3.4':
 | 
					  '@kurkle/color@0.3.4':
 | 
				
			||||||
    resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==}
 | 
					    resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@leveluptuts/svelte-fit@1.0.3':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-Hg8Xz06Mf1pwI92cY60LdFKDjXUkix0KcNP5orgGjtV7ecfK4zZ8gwPHOSDioZt73/7Muj1O43QzvtK2oPwEfA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@marijn/find-cluster-break@1.0.2':
 | 
					  '@marijn/find-cluster-break@1.0.2':
 | 
				
			||||||
    resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==}
 | 
					    resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -885,6 +905,9 @@ packages:
 | 
				
			||||||
    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
 | 
					    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
 | 
				
			||||||
    engines: {node: '>=10'}
 | 
					    engines: {node: '>=10'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fitty@2.4.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-GNhWgImK4+wEkgEZjBkQMyu5NLSmmryg/CaRP7zYby+TWzCrUou6BHL+iqbjKzJRXMyzuJkH+LBB1+lh4oO77g==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  flat-cache@4.0.1:
 | 
					  flat-cache@4.0.1:
 | 
				
			||||||
    resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
 | 
					    resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
 | 
				
			||||||
    engines: {node: '>=16'}
 | 
					    engines: {node: '>=16'}
 | 
				
			||||||
| 
						 | 
					@ -1138,6 +1161,9 @@ packages:
 | 
				
			||||||
  json-stable-stringify-without-jsonify@1.0.1:
 | 
					  json-stable-stringify-without-jsonify@1.0.1:
 | 
				
			||||||
    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
 | 
					    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  just-memoize@2.2.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-zriv+MY+61RXT0QsrO1ZJtL5umouqqSWmCGBkp2wJm35kniunBAA4qhUKx8Lvg/QcwrF9xuw9E6PkevKFf4boQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  keyv@4.5.4:
 | 
					  keyv@4.5.4:
 | 
				
			||||||
    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
 | 
					    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1951,6 +1977,11 @@ snapshots:
 | 
				
			||||||
      '@humanfs/core': 0.19.1
 | 
					      '@humanfs/core': 0.19.1
 | 
				
			||||||
      '@humanwhocodes/retry': 0.3.1
 | 
					      '@humanwhocodes/retry': 0.3.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@humanspeak/svelte-virtual-list@0.2.6(svelte@5.34.8)':
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      esm-env: 1.2.2
 | 
				
			||||||
 | 
					      svelte: 5.34.8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@humanwhocodes/module-importer@1.0.1': {}
 | 
					  '@humanwhocodes/module-importer@1.0.1': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@humanwhocodes/retry@0.3.1': {}
 | 
					  '@humanwhocodes/retry@0.3.1': {}
 | 
				
			||||||
| 
						 | 
					@ -1976,6 +2007,8 @@ snapshots:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@kurkle/color@0.3.4': {}
 | 
					  '@kurkle/color@0.3.4': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@leveluptuts/svelte-fit@1.0.3': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@marijn/find-cluster-break@1.0.2': {}
 | 
					  '@marijn/find-cluster-break@1.0.2': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@nodelib/fs.scandir@2.1.5':
 | 
					  '@nodelib/fs.scandir@2.1.5':
 | 
				
			||||||
| 
						 | 
					@ -2608,6 +2641,8 @@ snapshots:
 | 
				
			||||||
      locate-path: 6.0.0
 | 
					      locate-path: 6.0.0
 | 
				
			||||||
      path-exists: 4.0.0
 | 
					      path-exists: 4.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fitty@2.4.2: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  flat-cache@4.0.1:
 | 
					  flat-cache@4.0.1:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      flatted: 3.3.3
 | 
					      flatted: 3.3.3
 | 
				
			||||||
| 
						 | 
					@ -2869,6 +2904,8 @@ snapshots:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  json-stable-stringify-without-jsonify@1.0.1: {}
 | 
					  json-stable-stringify-without-jsonify@1.0.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  just-memoize@2.2.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  keyv@4.5.4:
 | 
					  keyv@4.5.4:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      json-buffer: 3.0.1
 | 
					      json-buffer: 3.0.1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,6 @@
 | 
				
			||||||
	import Book from "@ui/components/bookshelf/Book.svelte";
 | 
						import Book from "@ui/components/bookshelf/Book.svelte";
 | 
				
			||||||
	import Bookshelf from "@ui/components/bookshelf/Bookshelf.svelte";
 | 
						import Bookshelf from "@ui/components/bookshelf/Bookshelf.svelte";
 | 
				
			||||||
	import BookStack from "@ui/components/bookshelf/BookStack.svelte";
 | 
						import BookStack from "@ui/components/bookshelf/BookStack.svelte";
 | 
				
			||||||
	import BookStackElement from "@ui/components/bookshelf/BookStackElement.svelte";
 | 
					 | 
				
			||||||
	import {
 | 
						import {
 | 
				
			||||||
		createMetadata,
 | 
							createMetadata,
 | 
				
			||||||
		setMetadataContext,
 | 
							setMetadataContext,
 | 
				
			||||||
| 
						 | 
					@ -22,6 +21,8 @@
 | 
				
			||||||
	import DateFilter from "@ui/components/DateFilter.svelte";
 | 
						import DateFilter from "@ui/components/DateFilter.svelte";
 | 
				
			||||||
	import Rating from "@ui/components/Rating.svelte";
 | 
						import Rating from "@ui/components/Rating.svelte";
 | 
				
			||||||
	import { v4 as uuidv4 } from "uuid";
 | 
						import { v4 as uuidv4 } from "uuid";
 | 
				
			||||||
 | 
						import memoize from "just-memoize";
 | 
				
			||||||
 | 
						import VirtualList from "@humanspeak/svelte-virtual-list";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	interface Props {
 | 
						interface Props {
 | 
				
			||||||
		plugin: BookTrackerPlugin;
 | 
							plugin: BookTrackerPlugin;
 | 
				
			||||||
| 
						 | 
					@ -32,11 +33,11 @@
 | 
				
			||||||
		id: string;
 | 
							id: string;
 | 
				
			||||||
		title: string;
 | 
							title: string;
 | 
				
			||||||
		subtitle?: string;
 | 
							subtitle?: string;
 | 
				
			||||||
		author: string;
 | 
							authors: string[];
 | 
				
			||||||
		width: number;
 | 
							width: number;
 | 
				
			||||||
		color: ColorName;
 | 
							color: ColorName;
 | 
				
			||||||
		design: (typeof designs)[number];
 | 
							design: (typeof designs)[number];
 | 
				
			||||||
		orientation: undefined | "tilted" | "on-display";
 | 
							orientation: "default" | "tilted" | "front";
 | 
				
			||||||
		file: TFile;
 | 
							file: TFile;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,12 +56,7 @@
 | 
				
			||||||
	const metadataStore = createMetadata(plugin, settings.statusFilter, true);
 | 
						const metadataStore = createMetadata(plugin, settings.statusFilter, true);
 | 
				
			||||||
	setMetadataContext(metadataStore);
 | 
						setMetadataContext(metadataStore);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const designs = [
 | 
						const designs = ["default", "dual-top-bands", "split-bands"] as const;
 | 
				
			||||||
		"default",
 | 
					 | 
				
			||||||
		"colored-spine",
 | 
					 | 
				
			||||||
		"dual-top-bands",
 | 
					 | 
				
			||||||
		"split-bands",
 | 
					 | 
				
			||||||
	] as const;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const randomDesign = () => randomElement(designs);
 | 
						const randomDesign = () => randomElement(designs);
 | 
				
			||||||
	const randomColor = () => randomElement(COLOR_NAMES);
 | 
						const randomColor = () => randomElement(COLOR_NAMES);
 | 
				
			||||||
| 
						 | 
					@ -68,11 +64,11 @@
 | 
				
			||||||
		const n = randomFloat();
 | 
							const n = randomFloat();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (n < 0.55) {
 | 
							if (n < 0.55) {
 | 
				
			||||||
			return undefined;
 | 
								return "default";
 | 
				
			||||||
		} else if (n < 0.8) {
 | 
							} else if (n < 0.8) {
 | 
				
			||||||
			return "tilted";
 | 
								return "tilted";
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			return "on-display";
 | 
								return "front";
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	const randomStackChance = () => randomFloat() > 0.9;
 | 
						const randomStackChance = () => randomFloat() > 0.9;
 | 
				
			||||||
| 
						 | 
					@ -91,23 +87,34 @@
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function getBookData(metadata: FileMetadata): BookData {
 | 
						const getBookData = memoize(
 | 
				
			||||||
		return {
 | 
							(metadata: FileMetadata): BookData => {
 | 
				
			||||||
			id: metadata.file.path,
 | 
								const orientation = randomOrientation();
 | 
				
			||||||
			title: metadata.frontmatter[settings.titleProperty],
 | 
					
 | 
				
			||||||
			subtitle: settings.subtitleProperty
 | 
								return {
 | 
				
			||||||
				? metadata.frontmatter[settings.subtitleProperty]
 | 
									id: metadata.file.path,
 | 
				
			||||||
				: undefined,
 | 
									title: metadata.frontmatter[settings.titleProperty],
 | 
				
			||||||
			author: metadata.frontmatter[settings.authorsProperty].join(", "),
 | 
									subtitle: settings.subtitleProperty
 | 
				
			||||||
			width: metadata.frontmatter[
 | 
										? metadata.frontmatter[settings.subtitleProperty]
 | 
				
			||||||
				settingsStore.settings.pageCountProperty
 | 
										: undefined,
 | 
				
			||||||
			],
 | 
									authors: metadata.frontmatter[settings.authorsProperty],
 | 
				
			||||||
			color: randomColor(),
 | 
									width: Math.min(
 | 
				
			||||||
			design: randomDesign(),
 | 
										Math.max(
 | 
				
			||||||
			orientation: randomOrientation(),
 | 
											20,
 | 
				
			||||||
			file: metadata.file,
 | 
											metadata.frontmatter[
 | 
				
			||||||
		};
 | 
												settingsStore.settings.pageCountProperty
 | 
				
			||||||
	}
 | 
											] / 10,
 | 
				
			||||||
 | 
										),
 | 
				
			||||||
 | 
										100,
 | 
				
			||||||
 | 
									),
 | 
				
			||||||
 | 
									color: randomColor(),
 | 
				
			||||||
 | 
									design: orientation === "front" ? "default" : randomDesign(),
 | 
				
			||||||
 | 
									orientation: randomOrientation(),
 | 
				
			||||||
 | 
									file: metadata.file,
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							(metadata: FileMetadata) => metadata.file.path,
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let view = $state(settings.defaultView);
 | 
						let view = $state(settings.defaultView);
 | 
				
			||||||
	let books: (BookData | BookStackData)[] = $state([]);
 | 
						let books: (BookData | BookStackData)[] = $state([]);
 | 
				
			||||||
| 
						 | 
					@ -156,13 +163,14 @@
 | 
				
			||||||
		<Bookshelf>
 | 
							<Bookshelf>
 | 
				
			||||||
			{#each books as book (book.id)}
 | 
								{#each books as book (book.id)}
 | 
				
			||||||
				{#if "books" in book}
 | 
									{#if "books" in book}
 | 
				
			||||||
					<BookStack totalChildren={book.books.length}>
 | 
										<BookStack>
 | 
				
			||||||
						{#each book.books as bookData (bookData.id)}
 | 
											{#each book.books as bookData (bookData.id)}
 | 
				
			||||||
							<BookStackElement
 | 
												<Book
 | 
				
			||||||
								title={bookData.title}
 | 
													title={bookData.title}
 | 
				
			||||||
								subtitle={bookData.subtitle}
 | 
													subtitle={bookData.subtitle}
 | 
				
			||||||
								color={bookData.color}
 | 
													color={bookData.color}
 | 
				
			||||||
								design={bookData.design}
 | 
													design={bookData.design}
 | 
				
			||||||
 | 
													orientation="flat"
 | 
				
			||||||
								onClick={() =>
 | 
													onClick={() =>
 | 
				
			||||||
									plugin.app.workspace
 | 
														plugin.app.workspace
 | 
				
			||||||
										.getLeaf("tab")
 | 
															.getLeaf("tab")
 | 
				
			||||||
| 
						 | 
					@ -174,7 +182,7 @@
 | 
				
			||||||
					<Book
 | 
										<Book
 | 
				
			||||||
						title={book.title}
 | 
											title={book.title}
 | 
				
			||||||
						subtitle={book.subtitle}
 | 
											subtitle={book.subtitle}
 | 
				
			||||||
						author={book.author}
 | 
											authors={book.authors}
 | 
				
			||||||
						width={book.width}
 | 
											width={book.width}
 | 
				
			||||||
						color={book.color}
 | 
											color={book.color}
 | 
				
			||||||
						design={book.design}
 | 
											design={book.design}
 | 
				
			||||||
| 
						 | 
					@ -275,7 +283,7 @@
 | 
				
			||||||
	{/if}
 | 
						{/if}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style>
 | 
					<style lang="scss">
 | 
				
			||||||
	.shelf-code-block {
 | 
						.shelf-code-block {
 | 
				
			||||||
		.controls {
 | 
							.controls {
 | 
				
			||||||
			margin-bottom: 1rem;
 | 
								margin-bottom: 1rem;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,205 +1,327 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	import Self from "./Book.svelte";
 | 
						import type { Snippet } from "svelte";
 | 
				
			||||||
	import { Color, isColorName, type ColorName } from "@utils/color";
 | 
						import BookshelfItem from "./BookshelfItem.svelte";
 | 
				
			||||||
	import BookTilted from "./BookTilted.svelte";
 | 
						import { Color, type ColorName } from "@utils/color";
 | 
				
			||||||
	import BookOnDisplay from "./BookOnDisplay.svelte";
 | 
						import type { HTMLAttributes } from "svelte/elements";
 | 
				
			||||||
	import BookText from "./BookText.svelte";
 | 
						import { fit } from "@leveluptuts/svelte-fit";
 | 
				
			||||||
	const BOOK_SIZE_DEFAULT: number = 40;
 | 
					 | 
				
			||||||
	const BOOK_SIZE_MIN: number = 15;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	interface BookProps {
 | 
						interface Props {
 | 
				
			||||||
 | 
							children?: Snippet;
 | 
				
			||||||
		title?: string;
 | 
							title?: string;
 | 
				
			||||||
		subtitle?: string;
 | 
							subtitle?: string;
 | 
				
			||||||
		author?: string;
 | 
							authors?: string[];
 | 
				
			||||||
		color?: ColorName | string;
 | 
					 | 
				
			||||||
		design?: "default" | "colored-spine" | "dual-top-bands" | "split-bands";
 | 
					 | 
				
			||||||
		orientation?: "tilted" | "on-display";
 | 
					 | 
				
			||||||
		height?: number;
 | 
							height?: number;
 | 
				
			||||||
		width?: number;
 | 
							width?: number;
 | 
				
			||||||
 | 
							color?: ColorName;
 | 
				
			||||||
 | 
							orientation?: "default" | "tilted" | "flat" | "front";
 | 
				
			||||||
 | 
							design?: "default" | "split-bands" | "dual-top-bands";
 | 
				
			||||||
 | 
							role?: ARIAMixin["role"];
 | 
				
			||||||
 | 
							tabindex?: HTMLAttributes<HTMLDivElement>["tabindex"];
 | 
				
			||||||
		onClick?: () => void;
 | 
							onClick?: () => void;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let {
 | 
						let {
 | 
				
			||||||
		title,
 | 
							title,
 | 
				
			||||||
		subtitle,
 | 
							subtitle,
 | 
				
			||||||
		author,
 | 
							authors,
 | 
				
			||||||
		color: colorRaw = "green",
 | 
							height = 200,
 | 
				
			||||||
 | 
							width = 40,
 | 
				
			||||||
 | 
							color: colorName = "blue",
 | 
				
			||||||
 | 
							orientation = "default",
 | 
				
			||||||
		design = "default",
 | 
							design = "default",
 | 
				
			||||||
		orientation,
 | 
							role = "link",
 | 
				
			||||||
		height,
 | 
							tabindex = 0,
 | 
				
			||||||
		width,
 | 
					 | 
				
			||||||
		onClick,
 | 
							onClick,
 | 
				
			||||||
	}: BookProps = $props();
 | 
						}: Props = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function normalizeWidth(input: number | undefined) {
 | 
						$effect(() => {
 | 
				
			||||||
		if (input) {
 | 
							if (orientation === "front" && design !== "default") {
 | 
				
			||||||
			if (input <= 150) {
 | 
								console.warn(
 | 
				
			||||||
				return BOOK_SIZE_MIN;
 | 
									"The front orientation does not support different designs.",
 | 
				
			||||||
			}
 | 
								);
 | 
				
			||||||
			return input / 10;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return BOOK_SIZE_DEFAULT;
 | 
						});
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const color = $derived(
 | 
						const color = $derived(Color.fromName(colorName));
 | 
				
			||||||
		isColorName(colorRaw)
 | 
						const backgroundColor = $derived(color.chroma.css());
 | 
				
			||||||
			? Color.fromName(colorRaw).darken()
 | 
						const textColor = $derived(color.contrastColor.chroma.css());
 | 
				
			||||||
			: Color.fromCSSColor(colorRaw),
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const backgroundColor = $derived(
 | 
					 | 
				
			||||||
		design === "colored-spine"
 | 
					 | 
				
			||||||
			? color.chroma.mix("black", 0.14)
 | 
					 | 
				
			||||||
			: color.chroma,
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
	const borderLeftColor = $derived(color.chroma.mix("white", 0.04));
 | 
					 | 
				
			||||||
	const borderRightColor = $derived(color.chroma.mix("black", 0.04));
 | 
					 | 
				
			||||||
	const bandColor = $derived(color.chroma.mix("black", 0.14));
 | 
					 | 
				
			||||||
	const textColor = $derived(new Color(backgroundColor).contrastColor.hex);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const verifiedWidth = $derived(normalizeWidth(width));
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{#if orientation}
 | 
					<BookshelfItem>
 | 
				
			||||||
	{#if orientation === "tilted"}
 | 
						<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
 | 
				
			||||||
		<BookTilted {design} width={verifiedWidth}>
 | 
					 | 
				
			||||||
			<Self
 | 
					 | 
				
			||||||
				{title}
 | 
					 | 
				
			||||||
				{subtitle}
 | 
					 | 
				
			||||||
				{author}
 | 
					 | 
				
			||||||
				color={color.hex}
 | 
					 | 
				
			||||||
				{design}
 | 
					 | 
				
			||||||
				{height}
 | 
					 | 
				
			||||||
				{width}
 | 
					 | 
				
			||||||
				{onClick}
 | 
					 | 
				
			||||||
			/>
 | 
					 | 
				
			||||||
		</BookTilted>
 | 
					 | 
				
			||||||
	{:else if orientation === "on-display"}
 | 
					 | 
				
			||||||
		<BookOnDisplay color={color.hex} {onClick}>
 | 
					 | 
				
			||||||
			{#if title}
 | 
					 | 
				
			||||||
				<BookText {title} {subtitle} allowWrap />
 | 
					 | 
				
			||||||
				<p class="bookshelf__book-author">By: {author}</p>
 | 
					 | 
				
			||||||
			{/if}
 | 
					 | 
				
			||||||
		</BookOnDisplay>
 | 
					 | 
				
			||||||
	{/if}
 | 
					 | 
				
			||||||
{:else}
 | 
					 | 
				
			||||||
	<div
 | 
						<div
 | 
				
			||||||
		class="bookshelf__book-wrapper"
 | 
							class="book"
 | 
				
			||||||
		class:default={design === "default"}
 | 
							class:tilted={orientation === "tilted"}
 | 
				
			||||||
		class:colored-spine={design === "colored-spine"}
 | 
							class:flat={orientation === "flat"}
 | 
				
			||||||
		class:dual-top-bands={design === "dual-top-bands"}
 | 
							class:front={orientation === "front"}
 | 
				
			||||||
		class:split-bands={design === "split-bands"}
 | 
							style:--book-height="{height}px"
 | 
				
			||||||
		style:--book-color={backgroundColor.css()}
 | 
							style:--book-width="{width}px"
 | 
				
			||||||
		style:--book-border-left-color={borderLeftColor.css()}
 | 
							style:--book-bg-color={backgroundColor}
 | 
				
			||||||
		style:--book-border-right-color={borderRightColor.css()}
 | 
							style:--book-text-color={textColor}
 | 
				
			||||||
		style:--book-band-color={bandColor.css()}
 | 
							style:cursor={onClick ? "pointer" : "default"}
 | 
				
			||||||
		style:--book-width={verifiedWidth + "px"}
 | 
							{role}
 | 
				
			||||||
		style:width={verifiedWidth + "px"}
 | 
							{tabindex}
 | 
				
			||||||
		style:color={textColor}
 | 
					 | 
				
			||||||
		onclick={onClick}
 | 
							onclick={onClick}
 | 
				
			||||||
		onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
 | 
							onkeypress={(ev) => ev.key === "Enter" && onClick?.()}
 | 
				
			||||||
		role="link"
 | 
					 | 
				
			||||||
		tabindex="0"
 | 
					 | 
				
			||||||
	>
 | 
						>
 | 
				
			||||||
		<BookText {title} {subtitle} />
 | 
							{#if orientation !== "front" && (design === "split-bands" || design === "dual-top-bands")}
 | 
				
			||||||
 | 
								<div class="book-band top"></div>
 | 
				
			||||||
 | 
							{/if}
 | 
				
			||||||
 | 
							{#if orientation !== "front" && design === "dual-top-bands"}
 | 
				
			||||||
 | 
								<div class="book-band top2"></div>
 | 
				
			||||||
 | 
							{/if}
 | 
				
			||||||
 | 
							{#if orientation === "front"}
 | 
				
			||||||
 | 
								<div class="book-crease"></div>
 | 
				
			||||||
 | 
							{/if}
 | 
				
			||||||
 | 
							<div class="book-inner">
 | 
				
			||||||
 | 
								{#if title}
 | 
				
			||||||
 | 
									<h2 class="book-title">
 | 
				
			||||||
 | 
										{title}
 | 
				
			||||||
 | 
									</h2>
 | 
				
			||||||
 | 
									{#if subtitle}
 | 
				
			||||||
 | 
										<h3 class="book-subtitle">
 | 
				
			||||||
 | 
											{subtitle}
 | 
				
			||||||
 | 
										</h3>
 | 
				
			||||||
 | 
									{/if}
 | 
				
			||||||
 | 
								{/if}
 | 
				
			||||||
 | 
								{#if authors && orientation === "front"}
 | 
				
			||||||
 | 
									<p class="book-authors">By: {authors?.join(", ")}</p>
 | 
				
			||||||
 | 
								{/if}
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							{#if orientation !== "front" && design === "split-bands"}
 | 
				
			||||||
 | 
								<div class="book-band bottom"></div>
 | 
				
			||||||
 | 
							{/if}
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
{/if}
 | 
					</BookshelfItem>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="scss">
 | 
					<style lang="scss">
 | 
				
			||||||
	div.bookshelf__book-wrapper {
 | 
						@use "./variables.scss" as bookshelf;
 | 
				
			||||||
		background: var(--book-color);
 | 
					 | 
				
			||||||
		border-left: 2px solid var(--book-border-left-color);
 | 
					 | 
				
			||||||
		border-right: 2px solid var(--book-border-right-color);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		&.default,
 | 
						$small-band-width: calc(bookshelf.$book-band-width / 1.5);
 | 
				
			||||||
		&.colored-spine {
 | 
						$band-color: rgba(0, 0, 0, 0.2);
 | 
				
			||||||
			:global(.bookshelf__book-content) {
 | 
						$band-top-offset: bookshelf.$book-band-offset;
 | 
				
			||||||
				height: calc(var(--book-width));
 | 
						$band-top2-offset: $band-top-offset + $small-band-width +
 | 
				
			||||||
				margin: 0 var(--book-width);
 | 
							bookshelf.$book-band-gap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// prettier-ignore
 | 
				
			||||||
 | 
						:global(:has(.book:not(.tilted)) + :has(.book.tilted:hover):has(+ * > .book.tilted)),
 | 
				
			||||||
 | 
						:global(:has(.book.tilted) + :has(.book.tilted:not(:hover))) {
 | 
				
			||||||
 | 
							& > div.book.tilted {
 | 
				
			||||||
 | 
								margin-left: 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// prettier-ignore
 | 
				
			||||||
 | 
						:global(:has(.book.tilted) + :has(.book.tilted:hover):has(+ * > .book:not(.tilted))),
 | 
				
			||||||
 | 
						:global(:has(.book.tilted:not(:hover)):has(+ * > .book.tilted)) {
 | 
				
			||||||
 | 
							div.book.tilted {
 | 
				
			||||||
 | 
								margin-right: 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						div.book {
 | 
				
			||||||
 | 
							box-sizing: content-box;
 | 
				
			||||||
 | 
							position: relative;
 | 
				
			||||||
 | 
							height: var(--book-height);
 | 
				
			||||||
 | 
							width: var(--book-width);
 | 
				
			||||||
 | 
							border-radius: bookshelf.$book-corner-radius;
 | 
				
			||||||
 | 
							transition: transform 0.4s ease;
 | 
				
			||||||
 | 
							background-color: var(--book-bg-color);
 | 
				
			||||||
 | 
							color: var(--book-text-color);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.book-inner {
 | 
				
			||||||
 | 
								--book-content-width: calc(
 | 
				
			||||||
 | 
									var(--book-height) - #{bookshelf.$book-padding * 2}
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								box-sizing: border-box;
 | 
				
			||||||
 | 
								height: var(--book-width);
 | 
				
			||||||
 | 
								width: var(--book-height);
 | 
				
			||||||
 | 
								padding: bookshelf.$book-padding;
 | 
				
			||||||
 | 
								transform-origin: 0 0;
 | 
				
			||||||
 | 
								transform: translateX(var(--book-width)) rotate(90deg);
 | 
				
			||||||
 | 
								display: flex;
 | 
				
			||||||
 | 
								flex-direction: column;
 | 
				
			||||||
 | 
								justify-content: center;
 | 
				
			||||||
 | 
								align-items: center;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								$content-height: calc(
 | 
				
			||||||
 | 
									var(--book-width) - #{bookshelf.$book-padding * 2}
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								.book-title,
 | 
				
			||||||
 | 
								.book-subtitle,
 | 
				
			||||||
 | 
								.book-authors {
 | 
				
			||||||
 | 
									margin: 0;
 | 
				
			||||||
 | 
									padding: 0;
 | 
				
			||||||
 | 
									text-align: center;
 | 
				
			||||||
 | 
									display: flex;
 | 
				
			||||||
 | 
									justify-content: center;
 | 
				
			||||||
 | 
									align-items: center;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								.book-title {
 | 
				
			||||||
 | 
									font-size: 16px;
 | 
				
			||||||
 | 
									height: $content-height;
 | 
				
			||||||
 | 
									width: var(--book-content-width);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&:has(+ .book-subtitle) {
 | 
				
			||||||
 | 
										height: calc(#{$content-height} / 2);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								.book-subtitle {
 | 
				
			||||||
 | 
									font-size: 12px;
 | 
				
			||||||
 | 
									height: calc(#{$content-height} / 2);
 | 
				
			||||||
 | 
									width: var(--book-content-width);
 | 
				
			||||||
 | 
									align-items: start;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								.book-authors {
 | 
				
			||||||
 | 
									font-size: 10px;
 | 
				
			||||||
 | 
									justify-self: end;
 | 
				
			||||||
 | 
									width: var(--book-content-width);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		&.colored-spine {
 | 
							&:hover {
 | 
				
			||||||
			&:before {
 | 
								transform: scale(bookshelf.$book-hover-scale);
 | 
				
			||||||
				content: " ";
 | 
							}
 | 
				
			||||||
				display: block;
 | 
					 | 
				
			||||||
				background: var(--book-band-color);
 | 
					 | 
				
			||||||
				height: 100%;
 | 
					 | 
				
			||||||
				width: calc(var(--book-width));
 | 
					 | 
				
			||||||
				border-radius: 4px;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				position: absolute;
 | 
							&.tilted {
 | 
				
			||||||
				top: 0px;
 | 
								@include bookshelf.tilt;
 | 
				
			||||||
				left: -2px;
 | 
					
 | 
				
			||||||
 | 
								&:hover {
 | 
				
			||||||
 | 
									transform: scale(bookshelf.$book-hover-scale);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		&.dual-top-bands,
 | 
							// Flat book orientation for Book Stack
 | 
				
			||||||
		&.split-bands {
 | 
							&.flat {
 | 
				
			||||||
			&:after,
 | 
								height: var(--book-width);
 | 
				
			||||||
			&:before {
 | 
								width: var(--book-height);
 | 
				
			||||||
				content: "";
 | 
					 | 
				
			||||||
				display: block;
 | 
					 | 
				
			||||||
				background: var(--book-band-color);
 | 
					 | 
				
			||||||
				width: calc(100% + 4px);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				position: absolute;
 | 
								.book-inner {
 | 
				
			||||||
				left: -2px;
 | 
									height: var(--book-width);
 | 
				
			||||||
 | 
									width: var(--book-height);
 | 
				
			||||||
 | 
									transform: none;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			&:before {
 | 
								.book-band {
 | 
				
			||||||
				z-index: 2;
 | 
									transform: none;
 | 
				
			||||||
			}
 | 
									top: 0 !important;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			:global(.bookshelf__book-content),
 | 
									&.top {
 | 
				
			||||||
			:global(.fit-text) {
 | 
										left: $band-top-offset;
 | 
				
			||||||
				height: calc(var(--book-width));
 | 
					
 | 
				
			||||||
 | 
										&:has(+ .top2) {
 | 
				
			||||||
 | 
											width: $small-band-width;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&.top2 {
 | 
				
			||||||
 | 
										width: $small-band-width;
 | 
				
			||||||
 | 
										left: $band-top2-offset;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&.bottom {
 | 
				
			||||||
 | 
										left: calc(
 | 
				
			||||||
 | 
											var(--book-height) - #{bookshelf.$book-band-offset} - #{bookshelf.$book-band-width}
 | 
				
			||||||
 | 
										);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		&.dual-top-bands {
 | 
							// Front book orientation (cover view)
 | 
				
			||||||
			&:after {
 | 
							&.front {
 | 
				
			||||||
				height: 10px;
 | 
								$book-width: calc(var(--book-height) * 3 / 4);
 | 
				
			||||||
				top: 8px;
 | 
								width: $book-width;
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			&:before {
 | 
								.book-inner {
 | 
				
			||||||
				height: 15px;
 | 
									$padding-left: bookshelf.$book-crease-offset +
 | 
				
			||||||
				top: 26px;
 | 
										bookshelf.$book-crease-width + bookshelf.$book-padding;
 | 
				
			||||||
			}
 | 
									$padding-right: bookshelf.$book-padding;
 | 
				
			||||||
 | 
									$padding-total: $padding-left + $padding-right;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			:global(.bookshelf__book-content),
 | 
									--book-content-width: calc(#{$book-width} - #{$padding-total});
 | 
				
			||||||
			:global(.fit-text) {
 | 
									transform: none;
 | 
				
			||||||
				width: calc(200px - 61px) !important;
 | 
									height: var(--book-height);
 | 
				
			||||||
			}
 | 
									width: $book-width;
 | 
				
			||||||
 | 
									padding-left: $padding-left;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			:global(.bookshelf__book-content) {
 | 
									.book-title {
 | 
				
			||||||
				margin: 40px var(--book-width);
 | 
										height: calc(var(--book-height) / 4);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									.book-subtitle {
 | 
				
			||||||
 | 
										height: calc(var(--book-height) / 4);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									.book-authors {
 | 
				
			||||||
 | 
										height: calc(var(--book-height) / 2);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		&.split-bands {
 | 
							// Book band for styles
 | 
				
			||||||
			&:after,
 | 
							.book-band {
 | 
				
			||||||
			&:before {
 | 
								position: absolute;
 | 
				
			||||||
				height: 20px;
 | 
								height: var(--book-width);
 | 
				
			||||||
 | 
								width: bookshelf.$book-band-width;
 | 
				
			||||||
 | 
								transform-origin: 0 0;
 | 
				
			||||||
 | 
								transform: translateX(var(--book-width)) rotate(90deg);
 | 
				
			||||||
 | 
								background-color: $band-color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								&.top {
 | 
				
			||||||
 | 
									top: $band-top-offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&:has(+ .top2) {
 | 
				
			||||||
 | 
										width: $small-band-width;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			&:after {
 | 
								&.top2 {
 | 
				
			||||||
				top: 10px;
 | 
									width: $small-band-width;
 | 
				
			||||||
 | 
									top: $band-top2-offset;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			&:before {
 | 
								&.bottom {
 | 
				
			||||||
				bottom: 10px;
 | 
									top: calc(
 | 
				
			||||||
 | 
										var(--book-height) - #{bookshelf.$book-band-offset} - #{bookshelf.$book-band-width}
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			:global(.bookshelf__book-content),
 | 
								// Dual Top Band Offset
 | 
				
			||||||
			:global(.fit-text) {
 | 
								& + .book-band + .book-inner {
 | 
				
			||||||
				width: calc(200px - 62px) !important;
 | 
									$padding-left: $band-top2-offset + $small-band-width +
 | 
				
			||||||
 | 
										bookshelf.$book-padding;
 | 
				
			||||||
 | 
									$padding-right: bookshelf.$book-padding;
 | 
				
			||||||
 | 
									$padding-total: $padding-left + $padding-right;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									--book-content-width: calc(
 | 
				
			||||||
 | 
										var(--book-height) - #{$padding-total}
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
									padding-left: $padding-left;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			:global(.bookshelf__book-content) {
 | 
								// Split Band Offset
 | 
				
			||||||
				margin: 30px var(--book-width);
 | 
								&.top + .book-inner {
 | 
				
			||||||
 | 
									$padding: $band-top-offset + bookshelf.$book-band-width +
 | 
				
			||||||
 | 
										bookshelf.$book-padding;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									--book-content-width: calc(
 | 
				
			||||||
 | 
										var(--book-height) - #{$padding * 2}
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
									padding-left: $padding;
 | 
				
			||||||
 | 
									padding-right: $padding;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							.book-crease {
 | 
				
			||||||
 | 
								position: absolute;
 | 
				
			||||||
 | 
								height: var(--book-height);
 | 
				
			||||||
 | 
								width: bookshelf.$book-crease-width;
 | 
				
			||||||
 | 
								left: bookshelf.$book-crease-offset;
 | 
				
			||||||
 | 
								background-color: $band-color;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,65 +0,0 @@
 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
	import type { Snippet } from "svelte";
 | 
					 | 
				
			||||||
	import chroma from "chroma-js";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	interface Props {
 | 
					 | 
				
			||||||
		children?: Snippet;
 | 
					 | 
				
			||||||
		color?: string;
 | 
					 | 
				
			||||||
		onClick?: () => void;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	let { children, color = "green", onClick }: Props = $props();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	let backgroundColor = $derived(chroma(color));
 | 
					 | 
				
			||||||
	let bandColor = $derived(chroma(color).mix("black", 0.14));
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<div
 | 
					 | 
				
			||||||
	class="bookshelf__book-wrapper bookshelf__book-onDisplay"
 | 
					 | 
				
			||||||
	style:--book-color={backgroundColor.css()}
 | 
					 | 
				
			||||||
	style:--book-band-color={bandColor.css()}
 | 
					 | 
				
			||||||
	onclick={onClick}
 | 
					 | 
				
			||||||
	onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
 | 
					 | 
				
			||||||
	role="link"
 | 
					 | 
				
			||||||
	tabindex="0"
 | 
					 | 
				
			||||||
>
 | 
					 | 
				
			||||||
	<div class="book-display-crease"></div>
 | 
					 | 
				
			||||||
	{@render children?.()}
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="scss">
 | 
					 | 
				
			||||||
	$band-width: 5px;
 | 
					 | 
				
			||||||
	$band-offset: 8px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	div.bookshelf__book-onDisplay {
 | 
					 | 
				
			||||||
		width: 150px;
 | 
					 | 
				
			||||||
		height: 200px;
 | 
					 | 
				
			||||||
		background: var(--book-color);
 | 
					 | 
				
			||||||
		display: flex;
 | 
					 | 
				
			||||||
		flex-flow: column wrap;
 | 
					 | 
				
			||||||
		justify-content: space-between;
 | 
					 | 
				
			||||||
		align-items: center;
 | 
					 | 
				
			||||||
		text-align: center;
 | 
					 | 
				
			||||||
		margin: 20px 2px 10px 2px;
 | 
					 | 
				
			||||||
		position: relative;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		padding-left: calc($band-offset + $band-width);
 | 
					 | 
				
			||||||
		padding-right: 8px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		&:before {
 | 
					 | 
				
			||||||
			content: " ";
 | 
					 | 
				
			||||||
			display: block;
 | 
					 | 
				
			||||||
			background: var(--book-band-color);
 | 
					 | 
				
			||||||
			width: $band-width;
 | 
					 | 
				
			||||||
			height: 100%;
 | 
					 | 
				
			||||||
			position: absolute;
 | 
					 | 
				
			||||||
			top: 0;
 | 
					 | 
				
			||||||
			left: $band-offset;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		&:global(.bookshelf__book-author) {
 | 
					 | 
				
			||||||
			font-size: 0.8em;
 | 
					 | 
				
			||||||
			margin-left: 13px;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,19 +1,31 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	import type { Snippet } from "svelte";
 | 
						import type { Snippet } from "svelte";
 | 
				
			||||||
 | 
						import BookshelfItem from "./BookshelfItem.svelte";
 | 
				
			||||||
	const MAX_CHILDREN = 5;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	interface Props {
 | 
						interface Props {
 | 
				
			||||||
		children?: Snippet;
 | 
							children?: Snippet;
 | 
				
			||||||
		totalChildren?: number;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let { children, totalChildren = 0 }: Props = $props();
 | 
						let { children }: Props = $props();
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<ul
 | 
					<BookshelfItem>
 | 
				
			||||||
	class="bookshelf__bookStack-wrapper"
 | 
						<div class="book-stack">
 | 
				
			||||||
	style:margin={`calc(20px + ${(MAX_CHILDREN - totalChildren) * 40}px) 1px 10px`}
 | 
							{@render children?.()}
 | 
				
			||||||
>
 | 
						</div>
 | 
				
			||||||
	{@render children?.()}
 | 
					</BookshelfItem>
 | 
				
			||||||
</ul>
 | 
					
 | 
				
			||||||
 | 
					<style lang="scss">
 | 
				
			||||||
 | 
						@use "./variables.scss" as bookshelf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						div.book-stack {
 | 
				
			||||||
 | 
							display: flex;
 | 
				
			||||||
 | 
							flex-direction: column;
 | 
				
			||||||
 | 
							align-items: center;
 | 
				
			||||||
 | 
							justify-content: end;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							:global(.bookshelf-item) {
 | 
				
			||||||
 | 
								height: auto;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,142 +0,0 @@
 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
	import { Color, isColorName, type ColorName } from "@utils/color";
 | 
					 | 
				
			||||||
	import BookText from "./BookText.svelte";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	interface Props {
 | 
					 | 
				
			||||||
		title?: string;
 | 
					 | 
				
			||||||
		subtitle?: string;
 | 
					 | 
				
			||||||
		color?: ColorName | string;
 | 
					 | 
				
			||||||
		design?: "default" | "split-bands" | "dual-top-bands" | "colored-spine";
 | 
					 | 
				
			||||||
		onClick?: () => void;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	let {
 | 
					 | 
				
			||||||
		title,
 | 
					 | 
				
			||||||
		subtitle,
 | 
					 | 
				
			||||||
		color: colorRaw = "green",
 | 
					 | 
				
			||||||
		design,
 | 
					 | 
				
			||||||
		onClick,
 | 
					 | 
				
			||||||
	}: Props = $props();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const color = $derived(
 | 
					 | 
				
			||||||
		isColorName(colorRaw)
 | 
					 | 
				
			||||||
			? Color.fromName(colorRaw).darken()
 | 
					 | 
				
			||||||
			: Color.fromCSSColor(colorRaw),
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const backgroundColor = $derived(
 | 
					 | 
				
			||||||
		design === "colored-spine"
 | 
					 | 
				
			||||||
			? color.chroma.mix("black", 0.14)
 | 
					 | 
				
			||||||
			: color.chroma,
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
	const borderLeftColor = $derived(color.chroma.mix("white", 0.04));
 | 
					 | 
				
			||||||
	const borderRightColor = $derived(color.chroma.mix("black", 0.04));
 | 
					 | 
				
			||||||
	const bandColor = $derived(color.chroma.mix("black", 0.14));
 | 
					 | 
				
			||||||
	const textColor = $derived(new Color(backgroundColor).contrastColor.hex);
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<li class="bookshelf__bookstack-elem">
 | 
					 | 
				
			||||||
	<div
 | 
					 | 
				
			||||||
		class="bookshelf__book-wrapper"
 | 
					 | 
				
			||||||
		class:default={design === "default"}
 | 
					 | 
				
			||||||
		class:colored-spine={design === "colored-spine"}
 | 
					 | 
				
			||||||
		class:dual-top-bands={design === "dual-top-bands"}
 | 
					 | 
				
			||||||
		class:split-bands={design === "split-bands"}
 | 
					 | 
				
			||||||
		style:--book-color={backgroundColor.css()}
 | 
					 | 
				
			||||||
		style:--book-border-left-color={borderLeftColor.css()}
 | 
					 | 
				
			||||||
		style:--book-border-right-color={borderRightColor.css()}
 | 
					 | 
				
			||||||
		style:--book-band-color={bandColor.css()}
 | 
					 | 
				
			||||||
		style:color={textColor}
 | 
					 | 
				
			||||||
		onclick={onClick}
 | 
					 | 
				
			||||||
		onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
 | 
					 | 
				
			||||||
		role="link"
 | 
					 | 
				
			||||||
		tabindex="0"
 | 
					 | 
				
			||||||
	>
 | 
					 | 
				
			||||||
		<BookText {title} {subtitle} />
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
</li>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="scss">
 | 
					 | 
				
			||||||
	div {
 | 
					 | 
				
			||||||
		background: var(--book-color);
 | 
					 | 
				
			||||||
		border-left: 2px solid var(--book-border-left-color);
 | 
					 | 
				
			||||||
		border-right: 2px solid var(--book-border-right-color);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		&.colored-spine {
 | 
					 | 
				
			||||||
			&:before {
 | 
					 | 
				
			||||||
				content: " ";
 | 
					 | 
				
			||||||
				display: block;
 | 
					 | 
				
			||||||
				background: var(--book-band-color);
 | 
					 | 
				
			||||||
				height: 40px;
 | 
					 | 
				
			||||||
				width: calc(100% + 4px);
 | 
					 | 
				
			||||||
				border-radius: 4px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				position: absolute;
 | 
					 | 
				
			||||||
				top: 0px;
 | 
					 | 
				
			||||||
				left: -2px;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		&.split-bands,
 | 
					 | 
				
			||||||
		&.dual-top-bands {
 | 
					 | 
				
			||||||
			&:after,
 | 
					 | 
				
			||||||
			&:before {
 | 
					 | 
				
			||||||
				content: " ";
 | 
					 | 
				
			||||||
				display: block;
 | 
					 | 
				
			||||||
				background: var(--book-band-color);
 | 
					 | 
				
			||||||
				height: 40px;
 | 
					 | 
				
			||||||
				border-radius: 4px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				position: absolute;
 | 
					 | 
				
			||||||
				top: 0px;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			&:before {
 | 
					 | 
				
			||||||
				z-index: 2;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		&.split-bands {
 | 
					 | 
				
			||||||
			&:after {
 | 
					 | 
				
			||||||
				width: 20px;
 | 
					 | 
				
			||||||
				left: 10px;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			&:before {
 | 
					 | 
				
			||||||
				width: 20px;
 | 
					 | 
				
			||||||
				right: 10px;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			:global(.bookshelf__book-content),
 | 
					 | 
				
			||||||
			:global(.fit-text) {
 | 
					 | 
				
			||||||
				width: calc(200px - 60px) !important;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			:global(.bookshelf__book-content) {
 | 
					 | 
				
			||||||
				margin: 0 30px;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		&.dual-top-bands {
 | 
					 | 
				
			||||||
			&:after {
 | 
					 | 
				
			||||||
				width: 10px;
 | 
					 | 
				
			||||||
				left: 6px;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			&:before {
 | 
					 | 
				
			||||||
				width: 15px;
 | 
					 | 
				
			||||||
				left: 24px;
 | 
					 | 
				
			||||||
				z-index: 2;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			:global(.bookshelf__book-content),
 | 
					 | 
				
			||||||
			:global(.fit-text) {
 | 
					 | 
				
			||||||
				width: calc(200px - 31px) !important;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			:global(.bookshelf__book-content) {
 | 
					 | 
				
			||||||
				margin: 0 31px;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,61 +0,0 @@
 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
	import { fitText } from "@ui/directives";
 | 
					 | 
				
			||||||
	import { wrap } from "module";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	interface Props {
 | 
					 | 
				
			||||||
		title?: string;
 | 
					 | 
				
			||||||
		subtitle?: string;
 | 
					 | 
				
			||||||
		allowWrap?: boolean;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	let { title, subtitle, allowWrap }: Props = $props();
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<div
 | 
					 | 
				
			||||||
	class="bookshelf__book-content"
 | 
					 | 
				
			||||||
	class:center-content={subtitle === undefined}
 | 
					 | 
				
			||||||
>
 | 
					 | 
				
			||||||
	{#if title}
 | 
					 | 
				
			||||||
		<h2
 | 
					 | 
				
			||||||
			class="bookshelf__book-title fit-text"
 | 
					 | 
				
			||||||
			class:wrap={allowWrap}
 | 
					 | 
				
			||||||
			use:fitText={{
 | 
					 | 
				
			||||||
				minFontSize: 12,
 | 
					 | 
				
			||||||
				maxFontSize: 16,
 | 
					 | 
				
			||||||
				multiLine: allowWrap,
 | 
					 | 
				
			||||||
				detectMultiLine: false,
 | 
					 | 
				
			||||||
			}}
 | 
					 | 
				
			||||||
		>
 | 
					 | 
				
			||||||
			{title}
 | 
					 | 
				
			||||||
		</h2>
 | 
					 | 
				
			||||||
	{/if}
 | 
					 | 
				
			||||||
	{#if subtitle}
 | 
					 | 
				
			||||||
		<h4
 | 
					 | 
				
			||||||
			class="bookshelf__book-subtitle fit-text"
 | 
					 | 
				
			||||||
			use:fitText={{
 | 
					 | 
				
			||||||
				minFontSize: 10,
 | 
					 | 
				
			||||||
				maxFontSize: 14,
 | 
					 | 
				
			||||||
				multiLine: allowWrap,
 | 
					 | 
				
			||||||
				detectMultiLine: false,
 | 
					 | 
				
			||||||
			}}
 | 
					 | 
				
			||||||
		>
 | 
					 | 
				
			||||||
			{subtitle}
 | 
					 | 
				
			||||||
		</h4>
 | 
					 | 
				
			||||||
	{/if}
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="scss">
 | 
					 | 
				
			||||||
	.bookshelf__book-title,
 | 
					 | 
				
			||||||
	.bookshelf__book-subtitle {
 | 
					 | 
				
			||||||
		width: 100% !important;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		:global(.textFitted) {
 | 
					 | 
				
			||||||
			width: 100%;
 | 
					 | 
				
			||||||
			word-wrap: break-word;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.bookshelf__book-subtitle {
 | 
					 | 
				
			||||||
		overflow: hidden;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,51 +0,0 @@
 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
	import type { Snippet } from "svelte";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	interface Props {
 | 
					 | 
				
			||||||
		children?: Snippet;
 | 
					 | 
				
			||||||
		width?: number;
 | 
					 | 
				
			||||||
		design?: "default" | "colored-spine" | "dual-top-bands" | "split-bands";
 | 
					 | 
				
			||||||
		topMargin?: number;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	let {
 | 
					 | 
				
			||||||
		children,
 | 
					 | 
				
			||||||
		width = 40,
 | 
					 | 
				
			||||||
		design = "default",
 | 
					 | 
				
			||||||
		topMargin,
 | 
					 | 
				
			||||||
	}: Props = $props();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	function getTopMargin(
 | 
					 | 
				
			||||||
		design: "default" | "colored-spine" | "dual-top-bands" | "split-bands",
 | 
					 | 
				
			||||||
	): number {
 | 
					 | 
				
			||||||
		switch (design) {
 | 
					 | 
				
			||||||
			case "split-bands":
 | 
					 | 
				
			||||||
				return 30;
 | 
					 | 
				
			||||||
			case "dual-top-bands":
 | 
					 | 
				
			||||||
				return 41;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<div
 | 
					 | 
				
			||||||
	class="bookshelf__book-wrapper bookshelf__book-tilted"
 | 
					 | 
				
			||||||
	style:--book-tilted-width={width + "px"}
 | 
					 | 
				
			||||||
	style:--book-tilted-top-margin={(topMargin ?? getTopMargin(design)) + "px"}
 | 
					 | 
				
			||||||
>
 | 
					 | 
				
			||||||
	{@render children?.()}
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="scss">
 | 
					 | 
				
			||||||
	div {
 | 
					 | 
				
			||||||
		:global(.bookshelf__book-content),
 | 
					 | 
				
			||||||
		:global(.fit-text) {
 | 
					 | 
				
			||||||
			height: calc(var(--book-tilted-width));
 | 
					 | 
				
			||||||
			width: calc(200px - 60px);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		:global(.bookshelf__book-content) {
 | 
					 | 
				
			||||||
			margin: var(--book-tilted-top-margin) var(--book-tilted-width);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,32 +1,88 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	import "./bookshelf.scss";
 | 
						import { type Snippet } from "svelte";
 | 
				
			||||||
	import type { Snippet } from "svelte";
 | 
						import type { ActionReturn } from "svelte/action";
 | 
				
			||||||
 | 
						import textFit from "textfit";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	type Props = {
 | 
						interface Props {
 | 
				
			||||||
		children?: Snippet;
 | 
							children?: Snippet;
 | 
				
			||||||
	};
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let { children }: Props = $props();
 | 
						let { children }: Props = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const color = "#a47148";
 | 
						const fitAllText = (el: HTMLDivElement): ActionReturn => {
 | 
				
			||||||
 | 
							const observer = new MutationObserver(() => {
 | 
				
			||||||
 | 
								requestAnimationFrame(() => {
 | 
				
			||||||
 | 
									textFit(el.getElementsByClassName("book-title"), {
 | 
				
			||||||
 | 
										minFontSize: 8,
 | 
				
			||||||
 | 
										maxFontSize: 16,
 | 
				
			||||||
 | 
										multiLine: true,
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									textFit(el.getElementsByClassName("book-subtitle"), {
 | 
				
			||||||
 | 
										minFontSize: 6,
 | 
				
			||||||
 | 
										maxFontSize: 14,
 | 
				
			||||||
 | 
										multiLine: true,
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							observer.observe(el, {
 | 
				
			||||||
 | 
								childList: true,
 | 
				
			||||||
 | 
								subtree: true,
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								destroy() {
 | 
				
			||||||
 | 
									observer.disconnect();
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="bookshelf__wrapper" style:--book-shelf-color={color}>
 | 
					<div class="bookshelf" use:fitAllText>
 | 
				
			||||||
	{@render children?.()}
 | 
						{@render children?.()}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="scss">
 | 
					<style lang="scss">
 | 
				
			||||||
	div {
 | 
						@use "sass:color";
 | 
				
			||||||
 | 
						@use "./variables.scss" as bookshelf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						div.bookshelf {
 | 
				
			||||||
 | 
							width: 80%;
 | 
				
			||||||
 | 
							margin: 0 auto;
 | 
				
			||||||
 | 
							min-height: 100vh;
 | 
				
			||||||
 | 
							min-height: calc(bookshelf.$height + bookshelf.$shelf-width);
 | 
				
			||||||
		background-image: linear-gradient(
 | 
							background-image: linear-gradient(
 | 
				
			||||||
			color-mix(in srgb, var(--book-shelf-color), black 32%),
 | 
								color.scale(bookshelf.$color, $lightness: -32%),
 | 
				
			||||||
			color-mix(in srgb, var(--book-shelf-color), black 30%) 220px,
 | 
								color.scale(bookshelf.$color, $lightness: -30%) bookshelf.$height,
 | 
				
			||||||
			color-mix(in srgb, var(--book-shelf-color), white 4%) 220px,
 | 
					
 | 
				
			||||||
			color-mix(in srgb, var(--book-shelf-color), white 4%) 222px,
 | 
								color.scale(bookshelf.$color, $lightness: bookshelf.$shadow-pct)
 | 
				
			||||||
			var(--book-shelf-color) 222px,
 | 
									bookshelf.$height,
 | 
				
			||||||
			var(--book-shelf-color) 228px,
 | 
								color.scale(bookshelf.$color, $lightness: bookshelf.$shadow-pct)
 | 
				
			||||||
			color-mix(in srgb, var(--book-shelf-color), black 4%) 228px,
 | 
									calc(bookshelf.$height + bookshelf.$shadow-width),
 | 
				
			||||||
			color-mix(in srgb, var(--book-shelf-color), black 4%) 230px
 | 
								bookshelf.$color bookshelf.$height,
 | 
				
			||||||
 | 
								bookshelf.$color calc(bookshelf.$height + bookshelf.$shelf-width),
 | 
				
			||||||
 | 
								color.scale(
 | 
				
			||||||
 | 
										bookshelf.$color,
 | 
				
			||||||
 | 
										$lightness: -1 * bookshelf.$shadow-pct
 | 
				
			||||||
 | 
									)
 | 
				
			||||||
 | 
									calc(
 | 
				
			||||||
 | 
										bookshelf.$height + bookshelf.$shelf-width -
 | 
				
			||||||
 | 
											bookshelf.$shadow-width
 | 
				
			||||||
 | 
									),
 | 
				
			||||||
 | 
								color.scale(
 | 
				
			||||||
 | 
										bookshelf.$color,
 | 
				
			||||||
 | 
										$lightness: -1 * bookshelf.$shadow-pct
 | 
				
			||||||
 | 
									)
 | 
				
			||||||
 | 
									calc(bookshelf.$height + bookshelf.$shelf-width)
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		border: 10px var(--book-shelf-color) solid;
 | 
							background-size: 10px calc(bookshelf.$height + bookshelf.$shelf-width);
 | 
				
			||||||
 | 
							border: 10px bookshelf.$color solid;
 | 
				
			||||||
 | 
							padding-left: bookshelf.$book-spacing;
 | 
				
			||||||
 | 
							padding-right: bookshelf.$book-spacing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							display: flex;
 | 
				
			||||||
 | 
							flex-wrap: wrap;
 | 
				
			||||||
 | 
							gap: bookshelf.$shelf-width bookshelf.$book-spacing;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,30 @@
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
						import type { Snippet } from "svelte";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						interface Props {
 | 
				
			||||||
 | 
							children?: Snippet;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let { children }: Props = $props();
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="bookshelf-item">
 | 
				
			||||||
 | 
						{@render children?.()}
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="scss">
 | 
				
			||||||
 | 
						@use "./variables.scss" as bookshelf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						div.bookshelf-item {
 | 
				
			||||||
 | 
							box-sizing: border-box;
 | 
				
			||||||
 | 
							height: bookshelf.$height;
 | 
				
			||||||
 | 
							display: flex;
 | 
				
			||||||
 | 
							align-items: end;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Why????
 | 
				
			||||||
 | 
							padding-bottom: 0.75px;
 | 
				
			||||||
 | 
							&:global(:has(.book.tilted)) {
 | 
				
			||||||
 | 
								padding-bottom: 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					@ -1,217 +0,0 @@
 | 
				
			||||||
$white: white;
 | 
					 | 
				
			||||||
$black: black;
 | 
					 | 
				
			||||||
$background: #dedede;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
$bookWidth: 40px;
 | 
					 | 
				
			||||||
$bookHeight: 200px;
 | 
					 | 
				
			||||||
$bookEdge: 2px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.bookshelf__wrapper {
 | 
					 | 
				
			||||||
	width: 80%;
 | 
					 | 
				
			||||||
	height: 100%;
 | 
					 | 
				
			||||||
	margin: 0 auto;
 | 
					 | 
				
			||||||
	overflow: hidden;
 | 
					 | 
				
			||||||
	background-size: 10px 230px;
 | 
					 | 
				
			||||||
	min-height: 230px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.bookshelf__bookStack-wrapper {
 | 
					 | 
				
			||||||
		width: 200px;
 | 
					 | 
				
			||||||
		height: 100%;
 | 
					 | 
				
			||||||
		display: inline-flex;
 | 
					 | 
				
			||||||
		flex-flow: column nowrap;
 | 
					 | 
				
			||||||
		list-style: none;
 | 
					 | 
				
			||||||
		float: left;
 | 
					 | 
				
			||||||
		margin: 0;
 | 
					 | 
				
			||||||
		padding: 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		.bookshelf__bookStack-outOfStock {
 | 
					 | 
				
			||||||
			background: #232323;
 | 
					 | 
				
			||||||
			color: #fff;
 | 
					 | 
				
			||||||
			height: 150px;
 | 
					 | 
				
			||||||
			padding: 0;
 | 
					 | 
				
			||||||
			margin: 50px 1px auto 1px;
 | 
					 | 
				
			||||||
			display: flex;
 | 
					 | 
				
			||||||
			flex-flow: column nowrap;
 | 
					 | 
				
			||||||
			justify-content: space-around;
 | 
					 | 
				
			||||||
			align-items: center;
 | 
					 | 
				
			||||||
			font-size: 1.1em;
 | 
					 | 
				
			||||||
			font-weight: 600;
 | 
					 | 
				
			||||||
			letter-spacing: 1.25px;
 | 
					 | 
				
			||||||
			border-radius: 6px;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		.bookshelf__bookstack-elem {
 | 
					 | 
				
			||||||
			margin-inline-start: 0;
 | 
					 | 
				
			||||||
			margin: 0;
 | 
					 | 
				
			||||||
			padding: 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			.bookshelf__book-wrapper {
 | 
					 | 
				
			||||||
				width: 100%;
 | 
					 | 
				
			||||||
				height: 40px;
 | 
					 | 
				
			||||||
				margin: 0;
 | 
					 | 
				
			||||||
				padding: 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				.bookshelf__book-content {
 | 
					 | 
				
			||||||
					height: $bookWidth;
 | 
					 | 
				
			||||||
					width: $bookHeight;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					transform-origin: 0% 0%;
 | 
					 | 
				
			||||||
					transform: rotate(0deg);
 | 
					 | 
				
			||||||
					overflow: hidden;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					// * Centering content
 | 
					 | 
				
			||||||
					display: flex;
 | 
					 | 
				
			||||||
					flex-flow: column nowrap;
 | 
					 | 
				
			||||||
					justify-content: center;
 | 
					 | 
				
			||||||
					align-items: center;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					.bookshelf__book-title,
 | 
					 | 
				
			||||||
					.bookshelf__book-subtitle {
 | 
					 | 
				
			||||||
						font-size: 0.8em;
 | 
					 | 
				
			||||||
						font-weight: 600;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						height: calc($bookWidth / 2);
 | 
					 | 
				
			||||||
						width: $bookHeight;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						padding: 0;
 | 
					 | 
				
			||||||
						margin: 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						// * Centering content
 | 
					 | 
				
			||||||
						display: flex;
 | 
					 | 
				
			||||||
						justify-content: center;
 | 
					 | 
				
			||||||
						align-items: center;
 | 
					 | 
				
			||||||
						text-align: center;
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					.bookshelf__book-subtitle {
 | 
					 | 
				
			||||||
						font-size: 0.6em;
 | 
					 | 
				
			||||||
						letter-spacing: 1px;
 | 
					 | 
				
			||||||
						font-weight: 400;
 | 
					 | 
				
			||||||
						font-style: italic;
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				.center-content {
 | 
					 | 
				
			||||||
					display: flex;
 | 
					 | 
				
			||||||
					justify-content: center;
 | 
					 | 
				
			||||||
					align-items: center;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// .bookshelf__book-title{
 | 
					 | 
				
			||||||
				//     width: 140px;
 | 
					 | 
				
			||||||
				//     height: 40px;
 | 
					 | 
				
			||||||
				//     transform: rotate(0deg);
 | 
					 | 
				
			||||||
				//     margin-left: 29px;
 | 
					 | 
				
			||||||
				//     margin-top: 0;
 | 
					 | 
				
			||||||
				//     text-align: center;
 | 
					 | 
				
			||||||
				// }
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.bookshelf__book-wrapper {
 | 
					 | 
				
			||||||
		height: $bookHeight;
 | 
					 | 
				
			||||||
		width: $bookWidth;
 | 
					 | 
				
			||||||
		float: left;
 | 
					 | 
				
			||||||
		margin: 20px 1px 10px 1px;
 | 
					 | 
				
			||||||
		border-radius: 6px;
 | 
					 | 
				
			||||||
		transition: transform 0.4s ease;
 | 
					 | 
				
			||||||
		position: relative;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		.bookshelf__book-content {
 | 
					 | 
				
			||||||
			width: $bookHeight;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			transform-origin: 0% 0%;
 | 
					 | 
				
			||||||
			transform: rotate(90deg);
 | 
					 | 
				
			||||||
			overflow: hidden;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// * Centering content
 | 
					 | 
				
			||||||
			display: flex;
 | 
					 | 
				
			||||||
			flex-flow: column nowrap;
 | 
					 | 
				
			||||||
			justify-content: center;
 | 
					 | 
				
			||||||
			align-items: center;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			.bookshelf__book-title,
 | 
					 | 
				
			||||||
			.bookshelf__book-subtitle {
 | 
					 | 
				
			||||||
				font-size: 0.8em;
 | 
					 | 
				
			||||||
				font-weight: 600;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				height: calc($bookWidth / 2);
 | 
					 | 
				
			||||||
				width: $bookHeight;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				padding: 0;
 | 
					 | 
				
			||||||
				margin: 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// * Centering content
 | 
					 | 
				
			||||||
				display: flex;
 | 
					 | 
				
			||||||
				justify-content: center;
 | 
					 | 
				
			||||||
				align-items: center;
 | 
					 | 
				
			||||||
				text-align: center;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			.bookshelf__book-subtitle {
 | 
					 | 
				
			||||||
				font-size: 0.6em;
 | 
					 | 
				
			||||||
				letter-spacing: 1px;
 | 
					 | 
				
			||||||
				font-weight: 400;
 | 
					 | 
				
			||||||
				font-style: italic;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		&:hover {
 | 
					 | 
				
			||||||
			transform: scale(1.05);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		.center-content {
 | 
					 | 
				
			||||||
			display: flex;
 | 
					 | 
				
			||||||
			justify-content: center;
 | 
					 | 
				
			||||||
			align-items: center;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.bookshelf__book-tilted {
 | 
					 | 
				
			||||||
		float: left;
 | 
					 | 
				
			||||||
		width: 72px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		.bookshelf__book-wrapper {
 | 
					 | 
				
			||||||
			--book-width: 40px !important;
 | 
					 | 
				
			||||||
			width: 40px !important;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		&:hover .bookshelf__book-wrapper {
 | 
					 | 
				
			||||||
			transform: translateY(-20px);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		& > .bookshelf__book-wrapper {
 | 
					 | 
				
			||||||
			transform: translateY(-22px) translateX(13px) rotate(9deg);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	.bookshelf__book-onDisplay {
 | 
					 | 
				
			||||||
		.bookshelf__book-content {
 | 
					 | 
				
			||||||
			width: calc(100% - 11px);
 | 
					 | 
				
			||||||
			margin-left: 11px;
 | 
					 | 
				
			||||||
			transform: rotate(0deg);
 | 
					 | 
				
			||||||
			overflow: visible;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			.bookshelf__book-title,
 | 
					 | 
				
			||||||
			.bookshelf__book-subtitle {
 | 
					 | 
				
			||||||
				margin-top: 16px;
 | 
					 | 
				
			||||||
				width: 100%;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			.bookshelf__book-title {
 | 
					 | 
				
			||||||
				word-wrap: break-word;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			.bookshelf__book-subtitle {
 | 
					 | 
				
			||||||
				white-space: nowrap;
 | 
					 | 
				
			||||||
				text-overflow: ellipsis;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		.bookshelf__book-author {
 | 
					 | 
				
			||||||
			font-size: 0.8em;
 | 
					 | 
				
			||||||
			margin-left: 13px;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,47 @@
 | 
				
			||||||
 | 
					@use "sass:math";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$color: #a47148;
 | 
				
			||||||
 | 
					$height: 220px;
 | 
				
			||||||
 | 
					$shelf-width: 10px;
 | 
				
			||||||
 | 
					$shadow-pct: 4%;
 | 
				
			||||||
 | 
					$shadow-width: calc($shelf-width / 5);
 | 
				
			||||||
 | 
					$book-spacing: 2px;
 | 
				
			||||||
 | 
					$book-padding: 4px;
 | 
				
			||||||
 | 
					$book-corner-radius: 3.5px;
 | 
				
			||||||
 | 
					$book-hover-scale: 1.02;
 | 
				
			||||||
 | 
					$book-band-offset: 10px;
 | 
				
			||||||
 | 
					$book-band-gap: 5px;
 | 
				
			||||||
 | 
					$book-band-width: 10px;
 | 
				
			||||||
 | 
					$book-crease-offset: 10px;
 | 
				
			||||||
 | 
					$book-crease-width: 2px;
 | 
				
			||||||
 | 
					$tilt-angle: 9deg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@mixin tilt() {
 | 
				
			||||||
 | 
						$width: var(--book-width);
 | 
				
			||||||
 | 
						$height: var(--book-height);
 | 
				
			||||||
 | 
						$sin: math.sin($tilt-angle);
 | 
				
			||||||
 | 
						$cos: math.cos($tilt-angle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// $newWidth: calc(abs($width * $cos) + abs($height * $sin));
 | 
				
			||||||
 | 
						$newWidth: calc(
 | 
				
			||||||
 | 
							max(($width * $cos), -1 * ($width * $cos)) +
 | 
				
			||||||
 | 
								max(($height * $sin), -1 * ($height * $sin))
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
						$margin-h: calc(($newWidth - $width) / 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// $newHeight: calc(abs($width * $sin) + abs($height * $cos));
 | 
				
			||||||
 | 
						$newHeight: calc(
 | 
				
			||||||
 | 
							max(($width * $sin), -1 * ($width * $sin)) +
 | 
				
			||||||
 | 
								max(($height * $cos), -1 * ($height * $cos))
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
						$margin-v: calc(($newHeight - $height) / 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						transform: rotate($tilt-angle);
 | 
				
			||||||
 | 
						margin: $margin-v $margin-h;
 | 
				
			||||||
 | 
						transition: transform 0.4s ease, margin 0.4s ease;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						&:hover {
 | 
				
			||||||
 | 
							margin-top: 0;
 | 
				
			||||||
 | 
							margin-bottom: 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,16 +0,0 @@
 | 
				
			||||||
import type { ActionReturn } from "svelte/action";
 | 
					 | 
				
			||||||
import { default as doTextFit, type TextFitOption } from "textfit";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function fitText(
 | 
					 | 
				
			||||||
	el: HTMLElement,
 | 
					 | 
				
			||||||
	opts: TextFitOption = {}
 | 
					 | 
				
			||||||
): ActionReturn<TextFitOption> {
 | 
					 | 
				
			||||||
	doTextFit(el, opts);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return {
 | 
					 | 
				
			||||||
		update(opts) {
 | 
					 | 
				
			||||||
			doTextFit(el, opts);
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		destroy() {},
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,2 @@
 | 
				
			||||||
export { chart } from "./chart";
 | 
					export { chart } from "./chart";
 | 
				
			||||||
export { clickOutside } from "./clickOutside";
 | 
					export { clickOutside } from "./clickOutside";
 | 
				
			||||||
export { fitText } from "./fitText";
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue