Gisle Aune
4 years ago
5 changed files with 180 additions and 185 deletions
-
2svelte-ui/public/global.css
-
93svelte-ui/src/components/GroupEntry.svelte
-
22svelte-ui/src/components/LinkHook.svelte
-
120svelte-ui/src/components/ParentEntry.svelte
-
128svelte-ui/src/components/ProjectEntry.svelte
@ -1,96 +1,35 @@ |
|||||
<script lang="ts"> |
<script lang="ts"> |
||||
import { onMount } from "svelte"; |
|
||||
import type { IconName } from "../external/icons"; |
|
||||
import type { GroupResult } from "../models/group"; |
import type { GroupResult } from "../models/group"; |
||||
import type { ModalData } from "../stores/modal"; |
import type { ModalData } from "../stores/modal"; |
||||
import Icon from "./Icon.svelte"; |
|
||||
import ItemEntry from "./ItemEntry.svelte"; |
import ItemEntry from "./ItemEntry.svelte"; |
||||
import Option from "./Option.svelte"; |
import Option from "./Option.svelte"; |
||||
import OptionRow from "./OptionRow.svelte"; |
import OptionRow from "./OptionRow.svelte"; |
||||
|
import ParentEntry from "./ParentEntry.svelte"; |
||||
|
|
||||
export let group: GroupResult = null; |
export let group: GroupResult = null; |
||||
export let showAllOptions: boolean = false; |
export let showAllOptions: boolean = false; |
||||
|
|
||||
let iconName: IconName = "question"; |
|
||||
let mdItemAdd: ModalData; |
let mdItemAdd: ModalData; |
||||
let mdGroupEdit: ModalData; |
let mdGroupEdit: ModalData; |
||||
let mdGroupDelete: ModalData; |
let mdGroupDelete: ModalData; |
||||
let linkHook: HTMLElement; |
|
||||
|
|
||||
onMount(() => { |
|
||||
if (window.location.hash === "#" + group.id) { |
|
||||
window.scrollTo({top: linkHook.getBoundingClientRect().top}); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
$: iconName = group.icon as IconName; |
|
||||
$: mdItemAdd = {name:"item.add", group}; |
$: mdItemAdd = {name:"item.add", group}; |
||||
$: mdGroupEdit = {name:"group.edit", group}; |
$: mdGroupEdit = {name:"group.edit", group}; |
||||
$: mdGroupDelete = {name:"group.delete", group}; |
$: mdGroupDelete = {name:"group.delete", group}; |
||||
</script> |
</script> |
||||
|
|
||||
<div class="group"> |
|
||||
<div class="link-hook" id={group.id} bind:this={linkHook}></div> |
|
||||
<div class="icon"><Icon block name={iconName} /></div> |
|
||||
<div class="body"> |
|
||||
<div class="header"> |
|
||||
<div class="name">{group.name}</div> |
|
||||
</div> |
|
||||
{#if showAllOptions} |
|
||||
<div class="description"> |
|
||||
<p>{group.description}</p> |
|
||||
</div> |
|
||||
<OptionRow> |
|
||||
<Option open={mdItemAdd}>Add Item</Option> |
|
||||
<Option open={mdGroupEdit}>Edit</Option> |
|
||||
<Option open={mdGroupDelete}>Delete</Option> |
|
||||
</OptionRow> |
|
||||
{/if} |
|
||||
<div class="list" class:full={showAllOptions}> |
|
||||
{#each group.items as item (item.id)} |
|
||||
<ItemEntry item={item} group={group} /> |
|
||||
{/each} |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<style> |
|
||||
div.link-hook { |
|
||||
position: relative; |
|
||||
top: -2em; |
|
||||
} |
|
||||
|
|
||||
div.group { |
|
||||
display: flex; |
|
||||
flex-direction: row; |
|
||||
padding-bottom: 1em; |
|
||||
} |
|
||||
div.icon { |
|
||||
font-size: 2em; |
|
||||
padding: 0 0.5ch; |
|
||||
width: 2ch; |
|
||||
padding-top: 0.125em; |
|
||||
color: #333; |
|
||||
} |
|
||||
div.body { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
width: 100%; |
|
||||
} |
|
||||
div.header { |
|
||||
display: flex; |
|
||||
flex-direction: row; |
|
||||
} |
|
||||
|
|
||||
div.name { |
|
||||
font-size: 1em; |
|
||||
font-weight: 100; |
|
||||
margin: auto 0; |
|
||||
vertical-align: middle; |
|
||||
} |
|
||||
|
|
||||
div.description > p { |
|
||||
padding: 0; |
|
||||
margin: 0.25em 0; |
|
||||
} |
|
||||
</style> |
|
||||
|
<ParentEntry |
||||
|
full={showAllOptions} |
||||
|
entry={group} |
||||
|
> |
||||
|
{#if showAllOptions} |
||||
|
<OptionRow> |
||||
|
<Option open={mdItemAdd}>Add Item</Option> |
||||
|
<Option open={mdGroupEdit}>Edit</Option> |
||||
|
<Option open={mdGroupDelete}>Delete</Option> |
||||
|
</OptionRow> |
||||
|
{#each group.items as item (item.id)} |
||||
|
<ItemEntry item={item} group={group} /> |
||||
|
{/each} |
||||
|
{/if} |
||||
|
</ParentEntry> |
@ -0,0 +1,22 @@ |
|||||
|
<script lang="ts"> |
||||
|
import { onMount } from "svelte"; |
||||
|
|
||||
|
export let id = ""; |
||||
|
|
||||
|
let linkHook: HTMLElement; |
||||
|
|
||||
|
onMount(() => { |
||||
|
if (window.location.hash === "#" + id) { |
||||
|
window.scrollTo({top: linkHook.getBoundingClientRect().top}); |
||||
|
} |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<div class="link-hook" id={id} bind:this={linkHook}></div> |
||||
|
|
||||
|
<style> |
||||
|
div.link-hook { |
||||
|
position: relative; |
||||
|
top: -2em; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,120 @@ |
|||||
|
<script lang="ts"> |
||||
|
import type { IconName } from "../external/icons"; |
||||
|
import DaysLeft from "./DaysLeft.svelte"; |
||||
|
import Icon from "./Icon.svelte"; |
||||
|
import LinkHook from "./LinkHook.svelte"; |
||||
|
import Progress from "./Progress.svelte"; |
||||
|
|
||||
|
interface EntryIconHolder { |
||||
|
icon: IconName |
||||
|
} |
||||
|
|
||||
|
interface EntryCommon { |
||||
|
id: string |
||||
|
name: string |
||||
|
description: string |
||||
|
icon?: IconName |
||||
|
startTime?: string |
||||
|
endTime?: string |
||||
|
createdTime?: string |
||||
|
group?: EntryIconHolder |
||||
|
project?: EntryIconHolder |
||||
|
} |
||||
|
|
||||
|
export let entry: EntryCommon; |
||||
|
export let full = false; |
||||
|
export let headerLink = ""; |
||||
|
|
||||
|
export let progressAmount: number = null; |
||||
|
export let progressTarget: number = null; |
||||
|
|
||||
|
let iconName: IconName; |
||||
|
|
||||
|
$: { |
||||
|
if (entry.project != null) { |
||||
|
iconName = entry.project.icon; |
||||
|
} else if (entry.group != null) { |
||||
|
iconName = entry.group.icon; |
||||
|
} else { |
||||
|
iconName = entry.icon || "question"; |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div class="parent-entry" class:full={full}> |
||||
|
<LinkHook id={entry.id} /> |
||||
|
<div class="icon"><Icon block name={iconName} /></div> |
||||
|
<div class="body"> |
||||
|
<div class="header"> |
||||
|
<div class="name"> |
||||
|
{#if headerLink} |
||||
|
<a href={headerLink}>{entry.name}</a> |
||||
|
{:else} |
||||
|
{entry.name} |
||||
|
{/if} |
||||
|
</div> |
||||
|
{#if entry.endTime != null} |
||||
|
<div class="days-left"> |
||||
|
<DaysLeft startTime={entry.startTime || entry.createdTime} endTime={entry.endTime} /> |
||||
|
</div> |
||||
|
{/if} |
||||
|
</div> |
||||
|
{#if (progressAmount != null)} |
||||
|
<Progress thin green count={progressAmount} target={progressTarget} /> |
||||
|
{/if} |
||||
|
{#if (full)} |
||||
|
<div class="description"> |
||||
|
<p>{entry.description}</p> |
||||
|
</div> |
||||
|
{/if} |
||||
|
<slot></slot> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
div.parent-entry { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
padding-bottom: 0.5em; |
||||
|
} |
||||
|
div.parent-entry.full { |
||||
|
padding-bottom: 1em; |
||||
|
} |
||||
|
div.icon { |
||||
|
font-size: 2em; |
||||
|
padding: 0 0.5ch; |
||||
|
width: 2ch; |
||||
|
padding-top: 0.125em; |
||||
|
color: #333; |
||||
|
} |
||||
|
div.body { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
width: 100%; |
||||
|
} |
||||
|
div.header { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
} |
||||
|
|
||||
|
div.name { |
||||
|
font-size: 1em; |
||||
|
margin: auto 0; |
||||
|
vertical-align: middle; |
||||
|
font-weight: 100; |
||||
|
} |
||||
|
div.days-left { |
||||
|
margin-left: auto; |
||||
|
margin-right: 0.25ch; |
||||
|
} |
||||
|
|
||||
|
a { |
||||
|
color: inherit; |
||||
|
text-decoration-color: #777; |
||||
|
} |
||||
|
|
||||
|
div.description > p { |
||||
|
padding: 0; |
||||
|
margin: 0.25em 0; |
||||
|
} |
||||
|
</style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue