Compare commits

...

6 Commits

Author SHA1 Message Date
michal 86390d27c7 Add horrible storage system
continuous-integration/drone/push Build is passing
2022-05-04 23:46:50 +02:00
michal 47985a77d9 Fixing indentation 2022-05-04 22:25:48 +02:00
michal 2be16753bc Fix activity img CSS in description
continuous-integration/drone/push Build is passing
2022-04-12 09:00:31 +02:00
michal e18608ac2a Add image support
continuous-integration/drone/push Build is passing
2022-04-06 22:00:57 +02:00
michal 8cd26c971a Add support for more than 9 cards, with multi-page
continuous-integration/drone/push Build is passing
2022-04-06 21:39:54 +02:00
michal c9cd9f0da5 Change card CSS for readability 2022-04-06 21:38:50 +02:00
3 changed files with 131 additions and 27 deletions
+67 -10
View File
@@ -2,8 +2,27 @@
import Card from './Card.svelte'; import Card from './Card.svelte';
import CardInput from './CardInput.svelte'; import CardInput from './CardInput.svelte';
import { writable } from 'svelte/store';
import LZString from 'lz-string'; import LZString from 'lz-string';
const NO_CARD_MAGIC_STRING = "---";
let saved_cards = {};
let saved_cards_keys = [NO_CARD_MAGIC_STRING];
let selected_saved_card = NO_CARD_MAGIC_STRING;
const saved_cards_store = writable<object>(JSON.parse(localStorage.getItem("saved_cards")) || {} );
saved_cards_store.subscribe(v => {
if (Object.keys(v).length > 0) {
saved_cards = v;
saved_cards_keys = Object.keys(v).sort();
} else {
saved_cards = {};
saved_cards_keys = [NO_CARD_MAGIC_STRING]
}
selected_saved_card = saved_cards_keys[0];
})
saved_cards_store.update(n => n); // Dummy update
saved_cards_store.subscribe((value) => localStorage.setItem("saved_cards", JSON.stringify(value)));
const MAX_CARDS = 9; const MAX_CARDS = 9;
let cards = []; let cards = [];
let mhp = { let mhp = {
@@ -24,16 +43,21 @@
function get_idx_by_id(id) { function get_idx_by_id(id) {
for(let idx = 0; idx < cards.length; idx++) { for(let idx = 0; idx < cards.length; idx++) {
if(id === cards[idx].id) { if(id === cards[idx].id) {
return idx return idx;
} }
} }
} }
//let card_selected = 0; function chunk(arr) {
//$: selected_card = cards[card_selected]; let larr = [...arr], out = [];
while(larr.length) {
out = [...out, (larr.splice(0,MAX_CARDS))];
};
return out;
}
$: pages = chunk(cards);
let selector = cards[0].id; let selector = cards[0].id;
//$: selector = cards[card_selected].id;
$: selected_card = cards.filter(x => x.id === selector)[0]; $: selected_card = cards.filter(x => x.id === selector)[0];
$: selected_idx = get_idx_by_id(selector); $: selected_idx = get_idx_by_id(selector);
@@ -45,11 +69,9 @@
} }
function add_card() { function add_card() {
if (cards.length < MAX_CARDS) {
cards = [...cards, {id: cards[cards.length-1].id+1, ...mhp}]; cards = [...cards, {id: cards[cards.length-1].id+1, ...mhp}];
selector = cards[cards.length-1].id; selector = cards[cards.length-1].id;
} }
}
function remove_card() { function remove_card() {
console.log(cards); console.log(cards);
@@ -63,27 +85,61 @@
} }
} }
} }
function save_card() {
saved_cards_store.update(n => {
let to_save = {};
for (let key in selected_card) {
if (key !== "id") {
to_save[key] = selected_card[key];
}
}
n[to_save["name"]] = to_save;
return n;
});
}
function load_card() {
if (selected_saved_card !== NO_CARD_MAGIC_STRING) {
let new_card = saved_cards[selected_saved_card];
selected_card.name = new_card.name;
selected_card.type = new_card.type;
selected_card.tags = new_card.tags;
selected_card.attributes = new_card.attributes;
selected_card.description = new_card.description;
cards = cards;
}
}
function delete_card() {
saved_cards_store.update(n => {
delete n[selected_saved_card];
return n;
})
}
</script> </script>
<main> <main>
<section class="controls"> <section class="controls">
<h1>PF2e card generator</h1> <h1>PF2e card generator</h1>
<CardInput bind:name={selected_card.name} bind:type={selected_card.type} tags={selected_card.tags} bind:attributes={selected_card.attributes} bind:description={selected_card.description} on:change_tags={change_tags} on:add_card={add_card} on:remove_card={remove_card}/> <CardInput bind:name={selected_card.name} bind:type={selected_card.type} tags={selected_card.tags} bind:attributes={selected_card.attributes} bind:description={selected_card.description} bind:saved_cards={saved_cards_keys} bind:selected_saved_card={selected_saved_card} on:change_tags={change_tags} on:add_card={add_card} on:remove_card={remove_card} on:save_card={save_card} on:load_card={load_card} on:delete_card={delete_card}/>
</section> </section>
{#each pages as page}
<section class="cards"> <section class="cards">
{#each cards as card, idx (card.id)} {#each page as card, idx (card.id)}
<div class="card"> <div class="card" style="break-after:avoid;">
<Card {...card}/> <Card {...card}/>
<input class="card-selector" type="radio" bind:group={selector} value={card.id}/> <input class="card-selector" type="radio" bind:group={selector} value={card.id}/>
</div> </div>
{/each} {/each}
</section> </section>
{/each}
</main> </main>
<style> <style>
@media print { @media print {
@page { @page {
margin: 0; margin: 7mm;
} }
.controls { .controls {
display: none; display: none;
@@ -95,6 +151,7 @@
.cards { .cards {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
break-after: always;
} }
h1 { h1 {
text-transform: uppercase; text-transform: uppercase;
+14 -7
View File
@@ -28,6 +28,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
border-bottom: 2px solid black; border-bottom: 2px solid black;
padding: 0 4pt;
} }
.name, .type { .name, .type {
font-family: serif; font-family: serif;
@@ -35,8 +36,7 @@
font-weight: bold; font-weight: bold;
} }
.tags { .tags {
margin: 1pt 1pt 0pt 1pt; padding: 1pt 4pt;
padding: 0;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
} }
@@ -59,7 +59,10 @@
background-color: #54166e; background-color: #54166e;
} }
.content { .content {
margin: 1pt; margin-top: 1pt;
}
.content div {
padding: 0 4pt;
} }
:global(section.content p) { :global(section.content p) {
padding-bottom: 2pt; padding-bottom: 2pt;
@@ -72,6 +75,10 @@
:global(img.activity) { :global(img.activity) {
max-height: 8pt; max-height: 8pt;
} }
.description :global(img :not(.activity)) {
display: block;
margin: auto;
}
</style> </style>
@@ -94,12 +101,12 @@
{/each} {/each}
</section> </section>
<section class="content"> <section class="content">
<p class="attributes"> <div class="attributes">
{@html pf_filter(DOMPurify.sanitize(attributes))} {@html pf_filter(DOMPurify.sanitize(attributes))}
</p> </div>
<p class="description"> <div class="description">
{@html pf_filter(DOMPurify.sanitize(description))} {@html pf_filter(DOMPurify.sanitize(description))}
</p> </div>
</section> </section>
</div> </div>
+46 -6
View File
@@ -5,13 +5,44 @@
let apiKey = "xoifjvd46mymq6y2ev6wyaj7utyykb3tz9pf9v8m98oeiqip"; let apiKey = "xoifjvd46mymq6y2ev6wyaj7utyykb3tz9pf9v8m98oeiqip";
let conf = { let conf = {
"height": 500, height: 500,
"plugins": [ 'autoresize', 'code' ], plugins: [ 'autoresize', 'code', 'hr', 'image' ],
"toolbar": "undo redo | bold italic | code ", toolbar: "undo redo | bold italic | hr image | code ",
"menubar": false menubar: false,
file_picker_types: 'image',
/* and here's our custom image picker*/
file_picker_callback: function (cb, value, meta) {
var input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.onchange = function () {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function () {
/*
Note: Now we need to register the blob in TinyMCEs image blob
registry. In the next release this part hopefully won't be
necessary, as we are looking to handle it internally.
*/
var id = 'blobid' + (new Date()).getTime();
var blobCache = tinymce.activeEditor.editorUpload.blobCache;
var base64 = reader.result.split(',')[1];
var blobInfo = blobCache.create(id, file, base64);
blobCache.add(blobInfo);
/* call the callback and populate the Title field with the file name */
cb(blobInfo.blobUri(), { title: file.name });
};
reader.readAsDataURL(file);
}; };
export let name, type, tags, attributes, description; input.click();
},
};
export let name, type, tags, attributes, description, saved_cards, selected_saved_card;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
@@ -27,7 +58,6 @@
}); });
} }
</script> </script>
<style> <style>
div.cardInput { div.cardInput {
width: 5in; width: 5in;
@@ -46,8 +76,18 @@
<div class="row">Description: <div class="row">Description:
<Editor {apiKey} {conf} bind:value={description}/> <Editor {apiKey} {conf} bind:value={description}/>
</div> </div>
<div class="row">
<select bind:value={selected_saved_card}>
{#each saved_cards as card}
<option value={card}>{card}</option>
{/each}
</select>
</div>
<div class="row"> <div class="row">
<button on:click={e => dispatch('add_card')}>+</button> <button on:click={e => dispatch('add_card')}>+</button>
<button on:click={e => dispatch('remove_card')}>-</button> <button on:click={e => dispatch('remove_card')}>-</button>
<button on:click={e => dispatch('save_card')}>Save</button>
<button on:click={e => dispatch('load_card')}>Load</button>
<button on:click={e => dispatch('delete_card')}>Delete selected</button>
</div> </div>
</div> </div>