mirror of
https://github.com/edufeed-org/educards.git
synced 2025-12-07 23:34:34 +00:00
Add markdown rendering and improve styling a bit
This commit is contained in:
parent
218dfdd5c6
commit
cd23a61b90
9 changed files with 251 additions and 120 deletions
112
package-lock.json
generated
112
package-lock.json
generated
|
|
@ -8,9 +8,11 @@
|
||||||
"name": "educards",
|
"name": "educards",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@cartamd/plugin-emoji": "^4.3.0",
|
||||||
"@nostr-dev-kit/ndk": "^2.11.0",
|
"@nostr-dev-kit/ndk": "^2.11.0",
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
"carta-md": "^4.9.0",
|
"carta-md": "^4.9.0",
|
||||||
|
"marked": "^15.0.9",
|
||||||
"tailwindcss": "^4.1.4"
|
"tailwindcss": "^4.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
@ -46,6 +48,19 @@
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@cartamd/plugin-emoji": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@cartamd/plugin-emoji/-/plugin-emoji-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-D73qZP/55er1b08CVmBA385omOY9/88zwB1JKBdhEFmOZzYfwr42GiyGHcHpcRinKe/MiaWV4k8vEYj461/wOQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"bezier-easing": "^2.1.0",
|
||||||
|
"node-emoji": "^2.1.3",
|
||||||
|
"remark-emoji": "^5.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"carta-md": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.24.2",
|
"version": "0.24.2",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
|
||||||
|
|
@ -1215,6 +1230,17 @@
|
||||||
"resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz",
|
||||||
"integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="
|
"integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@sindresorhus/is": {
|
||||||
|
"version": "4.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
|
||||||
|
"integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@sveltejs/adapter-vercel": {
|
"node_modules/@sveltejs/adapter-vercel": {
|
||||||
"version": "5.6.1",
|
"version": "5.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-vercel/-/adapter-vercel-5.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-vercel/-/adapter-vercel-5.6.1.tgz",
|
||||||
|
|
@ -1766,6 +1792,11 @@
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/bezier-easing": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig=="
|
||||||
|
},
|
||||||
"node_modules/bindings": {
|
"node_modules/bindings": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||||
|
|
@ -1837,6 +1868,14 @@
|
||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/char-regex": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/character-entities": {
|
"node_modules/character-entities": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
|
||||||
|
|
@ -2084,6 +2123,20 @@
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz",
|
||||||
"integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="
|
"integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/emojilib": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw=="
|
||||||
|
},
|
||||||
|
"node_modules/emoticon": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ==",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/enhanced-resolve": {
|
"node_modules/enhanced-resolve": {
|
||||||
"version": "5.18.1",
|
"version": "5.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
|
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
|
||||||
|
|
@ -3133,6 +3186,17 @@
|
||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/marked": {
|
||||||
|
"version": "15.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/marked/-/marked-15.0.9.tgz",
|
||||||
|
"integrity": "sha512-9AW/bn9DxQeZVjR52l5jsc0W2pwuhP04QaQewPvylil12Cfr2GBfWmgp6mu8i9Jy8UlBjqDZ9uMTDuJ8QOGZJA==",
|
||||||
|
"bin": {
|
||||||
|
"marked": "bin/marked.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mdast-util-find-and-replace": {
|
"node_modules/mdast-util-find-and-replace": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
|
||||||
|
|
@ -3972,6 +4036,20 @@
|
||||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/node-emoji": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sindresorhus/is": "^4.6.0",
|
||||||
|
"char-regex": "^1.0.2",
|
||||||
|
"emojilib": "^2.4.0",
|
||||||
|
"skin-tone": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-fetch": {
|
"node_modules/node-fetch": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
|
@ -4541,6 +4619,21 @@
|
||||||
"url": "https://opencollective.com/unified"
|
"url": "https://opencollective.com/unified"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/remark-emoji": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-QCqTSvcZ65Ym+P+VyBKd4JfJfh7icMl7cIOGVmPMzWkDtdD8pQ0nQG7yxGolVIiMzSx90EZ7SwNiVpYpfTxn7w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/mdast": "^4.0.4",
|
||||||
|
"emoticon": "^4.0.1",
|
||||||
|
"mdast-util-find-and-replace": "^3.0.1",
|
||||||
|
"node-emoji": "^2.1.3",
|
||||||
|
"unified": "^11.0.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/remark-gfm": {
|
"node_modules/remark-gfm": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
|
||||||
|
|
@ -4800,6 +4893,17 @@
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/skin-tone": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==",
|
||||||
|
"dependencies": {
|
||||||
|
"unicode-emoji-modifier-base": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
|
|
@ -5176,6 +5280,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/typescript-lru-cache/-/typescript-lru-cache-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/typescript-lru-cache/-/typescript-lru-cache-2.0.0.tgz",
|
||||||
"integrity": "sha512-Jp57Qyy8wXeMkdNuZiglE6v2Cypg13eDA1chHwDG6kq51X7gk4K7P7HaDdzZKCxkegXkVHNcPD0n5aW6OZH3aA=="
|
"integrity": "sha512-Jp57Qyy8wXeMkdNuZiglE6v2Cypg13eDA1chHwDG6kq51X7gk4K7P7HaDdzZKCxkegXkVHNcPD0n5aW6OZH3aA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/unicode-emoji-modifier-base": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/unified": {
|
"node_modules/unified": {
|
||||||
"version": "11.0.5",
|
"version": "11.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,11 @@
|
||||||
"vite": "^6.0.0"
|
"vite": "^6.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@cartamd/plugin-emoji": "^4.3.0",
|
||||||
"@nostr-dev-kit/ndk": "^2.11.0",
|
"@nostr-dev-kit/ndk": "^2.11.0",
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
"carta-md": "^4.9.0",
|
"carta-md": "^4.9.0",
|
||||||
|
"marked": "^15.0.9",
|
||||||
"tailwindcss": "^4.1.4"
|
"tailwindcss": "^4.1.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,18 @@
|
||||||
<script>
|
<script>
|
||||||
// This is done in a single file for clarity. A more factored version here: https://svelte.dev/repl/288f827275db4054b23c437a572234f6?version=3.38.2
|
|
||||||
import { flip } from 'svelte/animate';
|
import { flip } from 'svelte/animate';
|
||||||
import { dndzone } from 'svelte-dnd-action';
|
import { dndzone } from 'svelte-dnd-action';
|
||||||
import AddColumnModal from './AddColumnModal.svelte';
|
import AddColumnModal from './AddColumnModal.svelte';
|
||||||
import AddCardModal from './AddCardModal.svelte';
|
import { cardsForColumn, columnsForBoard, currentBoard, user, userAndBoardMatch } from '$lib/db';
|
||||||
import { cardsForColumn, columnsForBoard, selectedColumn, currentBoard, user } from '$lib/db';
|
import { eventTitle, publishBoard } from '$lib/ndk';
|
||||||
import { eventTitle, publishBoard, publishCards } from '$lib/ndk';
|
import Column from './Column.svelte';
|
||||||
|
|
||||||
export let boardAddress;
|
export let boardAddress;
|
||||||
|
|
||||||
let addColumnModal;
|
let addColumnModal;
|
||||||
let addCardModal;
|
|
||||||
|
|
||||||
function openColumnModal() {
|
function openColumnModal() {
|
||||||
addColumnModal.showModal();
|
addColumnModal.showModal();
|
||||||
}
|
}
|
||||||
function openCardModal() {
|
|
||||||
addCardModal.showModal();
|
|
||||||
}
|
|
||||||
|
|
||||||
$: items = $columnsForBoard.map((col) => {
|
$: items = $columnsForBoard.map((col) => {
|
||||||
col.items = cardsForColumn(col.id);
|
col.items = cardsForColumn(col.id);
|
||||||
|
|
@ -35,95 +30,30 @@
|
||||||
// store current state
|
// store current state
|
||||||
publishBoard({ ...$currentBoard, items });
|
publishBoard({ ...$currentBoard, items });
|
||||||
}
|
}
|
||||||
function handleDndConsiderCards(cid, e) {
|
|
||||||
const colIdx = items.findIndex((c) => c.id === cid);
|
|
||||||
items[colIdx].items = e.detail.items;
|
|
||||||
items = [...items];
|
|
||||||
}
|
|
||||||
function handleDndFinalizeCards(cid, e) {
|
|
||||||
const colIdx = items.findIndex((c) => c.id === cid);
|
|
||||||
items[colIdx].items = e.detail.items;
|
|
||||||
items = [...items];
|
|
||||||
publishCards(items[colIdx]);
|
|
||||||
}
|
|
||||||
function handleClick(e) {
|
function handleClick(e) {
|
||||||
alert('dragabble elements are still clickable :)');
|
alert('dragabble elements are still clickable :)');
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteColumn(columnId) {
|
|
||||||
const updatedBoardItems = items.filter((e) => e.id !== columnId);
|
|
||||||
publishBoard({ ...$currentBoard, items: updatedBoardItems });
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteCard(column, itemId) {
|
|
||||||
const updatedColumnItems = column.items.filter((e) => e.id !== itemId);
|
|
||||||
publishCards({ ...column, items: updatedColumnItems });
|
|
||||||
}
|
|
||||||
|
|
||||||
function userAndBoardMatch(user, board) {
|
|
||||||
return user !== undefined && board && user?.pubkey === board?.pubkey;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-row items-center justify-between">
|
<div class="flex flex-row items-center justify-between">
|
||||||
<h1 class="ml-5 text-lg">{eventTitle($currentBoard)}</h1>
|
<h1 class="ml-5 text-lg font-bold">{eventTitle($currentBoard)}</h1>
|
||||||
{#if userAndBoardMatch($user, $currentBoard)}
|
{#if $user && userAndBoardMatch($user, $currentBoard)}
|
||||||
<button class="btn mr-5" on:click={openColumnModal}>Add column</button>
|
<button class="btn mr-5" on:click={openColumnModal}>Add column</button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<section
|
<section
|
||||||
class="board flex flex-nowrap"
|
class="board flex flex-nowrap gap-2"
|
||||||
use:dndzone={{ items, flipDurationMs, type: 'columns' }}
|
use:dndzone={{ items, flipDurationMs, type: 'columns' }}
|
||||||
on:consider={handleDndConsiderColumns}
|
on:consider={handleDndConsiderColumns}
|
||||||
on:finalize={handleDndFinalizeColumns}
|
on:finalize={handleDndFinalizeColumns}
|
||||||
>
|
>
|
||||||
{#each items as column (column.id)}
|
{#each items as column (column.id)}
|
||||||
<div class="column" animate:flip={{ duration: flipDurationMs }}>
|
<div class="w-96" animate:flip={{ duration: flipDurationMs }}>
|
||||||
{#if column !== undefined}
|
<Column {column} {flipDurationMs} bind:items />
|
||||||
<div class="flex">
|
|
||||||
<div class="column-title">{column.dTag}</div>
|
|
||||||
{#if userAndBoardMatch($user, $currentBoard)}
|
|
||||||
<button class="btn btn-error ml-auto mr-0" on:click={() => deleteColumn(column.id)}
|
|
||||||
>🗑️</button
|
|
||||||
>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
<div class="flex">
|
|
||||||
{#if userAndBoardMatch($user, $currentBoard)}
|
|
||||||
<button
|
|
||||||
class="btn mx-auto"
|
|
||||||
on:click={() => {
|
|
||||||
// set selected columns
|
|
||||||
$selectedColumn = column.dTag;
|
|
||||||
openCardModal();
|
|
||||||
}}>Add Card</button
|
|
||||||
>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if column.items.every((e) => e !== undefined)}
|
|
||||||
<div
|
|
||||||
class="column-content"
|
|
||||||
use:dndzone={{ items: column.items, flipDurationMs }}
|
|
||||||
on:consider={(e) => handleDndConsiderCards(column.id, e)}
|
|
||||||
on:finalize={(e) => handleDndFinalizeCards(column.id, e)}
|
|
||||||
>
|
|
||||||
<!-- {@debug column} -->
|
|
||||||
{#each column.items as item (item?.id ?? item)}
|
|
||||||
<div class="card" animate:flip={{ duration: flipDurationMs }}>
|
|
||||||
{eventTitle(item)}
|
|
||||||
{item.content}
|
|
||||||
<button on:click={() => deleteCard(column, item.id)}>Delete</button>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</section>
|
</section>
|
||||||
<AddColumnModal bind:modalRef={addColumnModal} />
|
<AddColumnModal bind:modalRef={addColumnModal} />
|
||||||
<AddCardModal bind:modalRef={addCardModal} />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.board {
|
.board {
|
||||||
|
|
@ -132,35 +62,4 @@
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
}
|
}
|
||||||
.column {
|
|
||||||
height: 100%;
|
|
||||||
width: 250px;
|
|
||||||
padding: 0.5em;
|
|
||||||
margin: 1em;
|
|
||||||
float: left;
|
|
||||||
border: 1px solid #333333;
|
|
||||||
/*Notice we make sure this container doesn't scroll so that the title stays on top and the dndzone inside is scrollable*/
|
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
|
||||||
.column-content {
|
|
||||||
height: 100%;
|
|
||||||
/* Notice that the scroll container needs to be the dndzone if you want dragging near the edge to trigger scrolling */
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
.column-title {
|
|
||||||
margin-bottom: 1em;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
height: 15%;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0.4em 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
background-color: #dddddd;
|
|
||||||
border: 1px solid #333333;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
17
src/lib/components/Card.svelte
Normal file
17
src/lib/components/Card.svelte
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<script>
|
||||||
|
import { eventTitle } from '$lib/ndk';
|
||||||
|
import { marked } from 'marked';
|
||||||
|
|
||||||
|
export let card;
|
||||||
|
|
||||||
|
function deleteCard(column, itemId) {
|
||||||
|
const updatedColumnItems = column.items.filter((e) => e.id !== itemId);
|
||||||
|
publishCards({ ...column, items: updatedColumnItems });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-1 rounded-xl border border-none bg-orange-300">
|
||||||
|
<h1 class="p-1 text-xl font-bold">{eventTitle(card)}</h1>
|
||||||
|
<p class="p-1">{@html marked.parse(card.content)}</p>
|
||||||
|
<button class="btn btn-warning m-1" on:click={() => deleteCard(column, item.id)}>Delete</button>
|
||||||
|
</div>
|
||||||
76
src/lib/components/Column.svelte
Normal file
76
src/lib/components/Column.svelte
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
<script>
|
||||||
|
import { dndzone } from 'svelte-dnd-action';
|
||||||
|
|
||||||
|
import Card from './Card.svelte';
|
||||||
|
import PlusCircleFill from '$lib/icons/PlusCircleFill.svelte';
|
||||||
|
import AddCardModal from './AddCardModal.svelte';
|
||||||
|
import { selectedColumn, currentBoard, user, userAndBoardMatch, deleteColumn } from '$lib/db';
|
||||||
|
import { eventTitle, publishCards } from '$lib/ndk';
|
||||||
|
|
||||||
|
export let column;
|
||||||
|
export let flipDurationMs;
|
||||||
|
export let items;
|
||||||
|
|
||||||
|
let addCardModal;
|
||||||
|
|
||||||
|
function openCardModal() {
|
||||||
|
addCardModal.showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDndConsiderCards(cid, e) {
|
||||||
|
const colIdx = items.findIndex((c) => c.id === cid);
|
||||||
|
items[colIdx].items = e.detail.items;
|
||||||
|
items = [...items];
|
||||||
|
}
|
||||||
|
function handleDndFinalizeCards(cid, e) {
|
||||||
|
const colIdx = items.findIndex((c) => c.id === cid);
|
||||||
|
items[colIdx].items = e.detail.items;
|
||||||
|
items = [...items];
|
||||||
|
publishCards(items[colIdx]);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if column !== undefined}
|
||||||
|
<div class="flex">
|
||||||
|
<div
|
||||||
|
class="flex w-full justify-center rounded-xl border border-none bg-orange-300 p-1 text-lg font-bold"
|
||||||
|
>
|
||||||
|
<p>{eventTitle(column)}</p>
|
||||||
|
</div>
|
||||||
|
{#if userAndBoardMatch($user, $currentBoard)}
|
||||||
|
<button class="btn btn-error mr-0 ml-auto" on:click={() => deleteColumn(column.id)}>🗑️</button
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="flex">
|
||||||
|
{#if userAndBoardMatch($user, $currentBoard)}
|
||||||
|
<button
|
||||||
|
class="btn bg-base-300 my-1 w-full border-none"
|
||||||
|
on:click={() => {
|
||||||
|
// set selected columns
|
||||||
|
$selectedColumn = column.dTag;
|
||||||
|
openCardModal();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlusCircleFill />
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if column.items.every((e) => e !== undefined)}
|
||||||
|
<div
|
||||||
|
class="h-full"
|
||||||
|
use:dndzone={{ items: column.items, flipDurationMs }}
|
||||||
|
on:consider={(e) => handleDndConsiderCards(column.id, e)}
|
||||||
|
on:finalize={(e) => handleDndFinalizeCards(column.id, e)}
|
||||||
|
>
|
||||||
|
{#each column.items as item (item?.id ?? item)}
|
||||||
|
<div class="my-1 p-1" animate:flip={{ duration: flipDurationMs }}>
|
||||||
|
<Card card={item} />
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<AddCardModal bind:modalRef={addCardModal} />
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
<script>
|
<script>
|
||||||
import { Carta, MarkdownEditor } from 'carta-md';
|
import { Carta, MarkdownEditor } from 'carta-md';
|
||||||
|
import { emoji } from '@cartamd/plugin-emoji';
|
||||||
// Component default theme
|
// Component default theme
|
||||||
import 'carta-md/default.css';
|
import 'carta-md/default.css';
|
||||||
|
|
||||||
const carta = new Carta();
|
const carta = new Carta({
|
||||||
|
extensions: [emoji()]
|
||||||
|
});
|
||||||
|
|
||||||
export let value = '';
|
export let value = '';
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,15 @@ export const boards = derived(events, ($events) => {
|
||||||
return deduped;
|
return deduped;
|
||||||
});
|
});
|
||||||
export const currentBoardAddress = writable('');
|
export const currentBoardAddress = writable('');
|
||||||
export const currentBoard = derived(currentBoardAddress, ($currentBoardAddress) => {
|
export const currentBoard = derived(
|
||||||
console.log('looking for current board');
|
[currentBoardAddress, events],
|
||||||
const board = get(events).find((e) => e.tagAddress() === $currentBoardAddress);
|
([$currentBoardAddress, $events]) => {
|
||||||
console.log(board);
|
console.log('looking for current board');
|
||||||
return board;
|
const board = get(events).find((e) => e.tagAddress() === $currentBoardAddress);
|
||||||
});
|
console.log(board);
|
||||||
|
return board;
|
||||||
|
}
|
||||||
|
);
|
||||||
export const columns = derived(events, ($events) => {
|
export const columns = derived(events, ($events) => {
|
||||||
const allColumns = $events.filter((e) => e.kind === 30044);
|
const allColumns = $events.filter((e) => e.kind === 30044);
|
||||||
const deduped = deduplicateKeepMostRecent(allColumns);
|
const deduped = deduplicateKeepMostRecent(allColumns);
|
||||||
|
|
@ -87,9 +90,9 @@ const createNDKStore = () => {
|
||||||
// 'wss://relay.damus.io'
|
// 'wss://relay.damus.io'
|
||||||
// 'wss://relay.nostr.band',
|
// 'wss://relay.nostr.band',
|
||||||
// 'wss://nos.lol',
|
// 'wss://nos.lol',
|
||||||
// 'ws://localhost:10547'
|
'ws://localhost:10547'
|
||||||
'wss://purplepag.es',
|
// 'wss://purplepag.es',
|
||||||
'wss://relay-k12.edufeed.org'
|
// 'wss://relay-k12.edufeed.org'
|
||||||
// Add more default relays here
|
// Add more default relays here
|
||||||
]
|
]
|
||||||
) => {
|
) => {
|
||||||
|
|
@ -170,3 +173,12 @@ function deduplicateKeepMostRecent(array) {
|
||||||
});
|
});
|
||||||
return Array.from(mostRecentMap.values());
|
return Array.from(mostRecentMap.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function userAndBoardMatch(user, board) {
|
||||||
|
return user !== undefined && board && user?.pubkey === board?.pubkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteColumn(columnId) {
|
||||||
|
const updatedBoardItems = items.filter((e) => e.id !== columnId);
|
||||||
|
publishBoard({ ...$currentBoard, items: updatedBoardItems });
|
||||||
|
}
|
||||||
|
|
|
||||||
11
src/lib/icons/PlusCircleFill.svelte
Normal file
11
src/lib/icons/PlusCircleFill.svelte
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
class="bi bi-plus-circle-fill"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0M8.5 4.5a.5.5 0 0 0-1 0v3h-3a.5.5 0 0 0 0 1h3v3a.5.5 0 0 0 1 0v-3h3a.5.5 0 0 0 0-1h-3z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 266 B |
|
|
@ -3,7 +3,6 @@
|
||||||
import { currentBoardAddress } from '$lib/db.js';
|
import { currentBoardAddress } from '$lib/db.js';
|
||||||
export let data;
|
export let data;
|
||||||
|
|
||||||
console.log(data.id);
|
|
||||||
$currentBoardAddress = data.id;
|
$currentBoardAddress = data.id;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue