+106
@@ -0,0 +1,106 @@
|
||||
<script lang="ts">
|
||||
import Card from './Card.svelte';
|
||||
import CardInput from './CardInput.svelte';
|
||||
|
||||
import LZString from 'lz-string';
|
||||
|
||||
const MAX_CARDS = 9;
|
||||
let cards = [];
|
||||
let mhp = {
|
||||
name: "Minor healing potion",
|
||||
type: "Item 1",
|
||||
tags: ["Consumable", "Healing", "Magical", "Necromancy", "Positive", "Potion"],
|
||||
attributes: `<p><b>Usage</b> Held in 1 hand; <b>Bulk</b> L</p>
|
||||
<p><b>Activate (A)</b> Interact</p>`,
|
||||
description: `<p>A healing potion is a vial of a ruby-red liquid that imparts a tingling sensation as the drinker’s wounds heal rapidly. When you drink a healing potion, you regain 2d8+5 Hit Points.</p>`
|
||||
};
|
||||
|
||||
if (window.location.hash !== "") {
|
||||
debugger;
|
||||
cards = JSON.parse(LZString.decompressFromEncodedURIComponent(window.location.hash.substring(1)));
|
||||
} else {
|
||||
cards = [{id: 0, ...mhp}];
|
||||
}
|
||||
|
||||
function get_idx_by_id(id) {
|
||||
for(let idx = 0; idx < cards.length; idx++) {
|
||||
if(id === cards[idx].id) {
|
||||
return idx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//let card_selected = 0;
|
||||
//$: selected_card = cards[card_selected];
|
||||
|
||||
let selector = cards[0].id;
|
||||
//$: selector = cards[card_selected].id;
|
||||
$: selected_card = cards.filter(x => x.id === selector)[0];
|
||||
$: selected_idx = get_idx_by_id(selector);
|
||||
|
||||
$: window.location.hash = LZString.compressToEncodedURIComponent(JSON.stringify(cards));
|
||||
|
||||
function change_tags(e) {
|
||||
selected_card.tags = e.detail.tags.split(',').map(x => x.replace(/\s+/g, '')).filter(x => x != '');
|
||||
cards[selected_idx] = selected_card;
|
||||
}
|
||||
|
||||
function add_card() {
|
||||
if (cards.length < MAX_CARDS) {
|
||||
cards = [...cards, {id: cards[cards.length-1].id+1, ...mhp}];
|
||||
selector = cards[cards.length-1].id;
|
||||
}
|
||||
}
|
||||
|
||||
function remove_card() {
|
||||
console.log(cards);
|
||||
console.log(selected_idx);
|
||||
if (cards.length > 1) { // remove only if there is more than 1 card
|
||||
cards = cards.filter(x => x.id != selector);
|
||||
if (selected_idx >= cards.length) {
|
||||
selector = cards[cards.length-1].id;
|
||||
} else {
|
||||
selector = cards[selected_idx].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<section class="controls">
|
||||
<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}/>
|
||||
</section>
|
||||
<section class="cards">
|
||||
{#each cards as card, idx (card.id)}
|
||||
<div class="card">
|
||||
<Card {...card}/>
|
||||
<input class="card-selector" type="radio" bind:group={selector} value={card.id}/>
|
||||
</div>
|
||||
{/each}
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
@media print {
|
||||
@page {
|
||||
margin: 0;
|
||||
}
|
||||
.controls {
|
||||
display: none;
|
||||
}
|
||||
.card-selector {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
h1 {
|
||||
text-transform: uppercase;
|
||||
font-size: 4em;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,75 @@
|
||||
<script lang="ts">
|
||||
import DOMPurify from 'dompurify';
|
||||
export let name:string, type, tags = [], attributes:string, description:string;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.card {
|
||||
width: 2.5in;
|
||||
height: 3.5in;
|
||||
font-size: 8pt;
|
||||
border: 2mm solid black;
|
||||
/*background-image: url("/bg.webp");*/
|
||||
background-clip: border-box;
|
||||
page-break-after: auto;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: 2px solid black;
|
||||
}
|
||||
.name, .type {
|
||||
font-family: serif;
|
||||
font-size: 10pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tags {
|
||||
margin: 1pt 1pt 0pt 1pt;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.tag {
|
||||
font-family: sans-serif;
|
||||
font-size: 6pt;
|
||||
font-variant: all-small-caps;
|
||||
color: white;
|
||||
background-color: #5e0000;
|
||||
padding: 0.5pt 1pt;
|
||||
border: 1pt solid #d9c484;
|
||||
}
|
||||
.content {
|
||||
margin: 1pt;
|
||||
}
|
||||
:global(section.content p) {
|
||||
padding-bottom: 2pt;
|
||||
margin: 0;
|
||||
}
|
||||
.description {
|
||||
border-top: 1pt solid black;
|
||||
text-align: justify;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<div class="card">
|
||||
<section class="header">
|
||||
<div class="name">{name}</div>
|
||||
<div class="type">{type}</div>
|
||||
</section>
|
||||
<section class="tags">
|
||||
{#each tags as tag}
|
||||
<div class="tag">{tag}</div>
|
||||
{/each}
|
||||
</section>
|
||||
<section class="content">
|
||||
<p class="attributes">
|
||||
{@html DOMPurify.sanitize(attributes)}
|
||||
</p>
|
||||
<p class="description">
|
||||
{@html DOMPurify.sanitize(description)}
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let name, type, tags, attributes, description;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function change_tags(e) {
|
||||
dispatch("change_tags", {tags: e.target.value});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
div.cardInput {
|
||||
width: 5in;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="cardInput">
|
||||
<div class="row">Name: <input name="name" bind:value={name}/></div>
|
||||
<div class="row">Type: <input name="type" bind:value={type}/></div>
|
||||
<div class="row">Tags: <input name="tags" on:change={change_tags} value={tags}/></div>
|
||||
<div class="row">Attributes:
|
||||
<textarea bind:value={attributes}></textarea>
|
||||
</div>
|
||||
<div class="row">Description:
|
||||
<textarea bind:value={description}></textarea>
|
||||
</div>
|
||||
<div class="row">
|
||||
<button on:click={e => dispatch('add_card')}>+</button>
|
||||
<button on:click={e => dispatch('remove_card')}>-</button>
|
||||
</div>
|
||||
</div>
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
/// <reference types="svelte" />
|
||||
@@ -0,0 +1,7 @@
|
||||
import App from './App.svelte';
|
||||
|
||||
const app = new App({
|
||||
target: document.body,
|
||||
});
|
||||
|
||||
export default app;
|
||||
Reference in New Issue
Block a user