Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow re-opening completed and dead-end modals #131

Merged
merged 1 commit into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/components/Menu/Icon.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script lang="ts">
import { icons, type IIconName } from "./icons";

export let icon: IIconName;
</script>

<svg viewBox="0 0 16 16">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1"
d={icons[icon].path}
/>
</svg>
37 changes: 7 additions & 30 deletions src/components/Menu/IconButton.svelte
Original file line number Diff line number Diff line change
@@ -1,24 +1,8 @@
<script lang="ts">
import { randomString } from "../../utils/components";

type IIconName = "Close" | "Help" | "Menu" | "Shuffle" | "Undo";
const icons: { [key in IIconName]: { path: string } } = {
Close: {
path: "M 3 3 L 13 13 M 3 13 L 13 3",
},
Help: {
path: "M 8 12 L 8 12 M 8 10 L 8 9 C 8 7 10 8 10 6 A 2 2 0 1 0 6 6 M 8 1.5 A 6.5 6.5 0 0 1 8 14.5 A 6.5 6.5 0 0 1 8 1.5",
},
Menu: {
path: "M 2 4 L 14 4 M 2 8 L 14 8 M 2 12 L 14 12",
},
Shuffle: {
path: "M 5 2 L 2 5 L 5 8 M 2 5 L 12 5 M 11 14 L 14 11 L 11 8 M 14 11 L 4 11",
},
Undo: {
path: "M 5 3 L 2 6 L 5 9 M 2 6 L 10 6 A 3 3 0 1 1 10 13 L 8 13",
},
};
import Icon from "./Icon.svelte";
import type { IIconName } from "./icons";

export let icon: IIconName;
export let label: string;
Expand All @@ -37,16 +21,9 @@
class:open
on:click={onClick}
>
<svg viewBox="0 0 16 16">
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1"
d={icons[icon].path}
/>
</svg>
<div class="icon">
<Icon {icon} />
</div>
</button>
<div class="label" id={labelId}>{label}</div>
</div>
Expand All @@ -65,7 +42,7 @@
&:hover .content
transform: scale(1.1) translateY(-0.15rem)

&:active svg
&:active .icon
transform: translateY(0.15rem)

div.content
Expand All @@ -89,7 +66,7 @@
outline: 3px solid black
border-radius: 0.5em

svg
:global(svg)
height: 2rem

div.label
Expand Down
31 changes: 31 additions & 0 deletions src/components/Menu/icons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export type IIconName =
| "Close"
| "Help"
| "Menu"
| "Minimize"
| "OpenModal"
| "Shuffle"
| "Undo";
export const icons: { [key in IIconName]: { path: string } } = {
Close: {
path: "M 3 3 L 13 13 M 3 13 L 13 3",
},
Help: {
path: "M 8 12 L 8 12 M 8 10 L 8 9 C 8 7 10 8 10 6 A 2 2 0 1 0 6 6 M 8 1.5 A 6.5 6.5 0 0 1 8 14.5 A 6.5 6.5 0 0 1 8 1.5",
},
Menu: {
path: "M 2 4 L 14 4 M 2 8 L 14 8 M 2 12 L 14 12",
},
Minimize: {
path: "M 11 13 L 6.75 13 M 11 11 L 6.75 11 M 11 11 L 11 6.75 M 11 11 L 3 3",
},
OpenModal: {
path: "M 3 3 L 7.25 3 M 3 3 L 3 7.25 M 3 3 L 10 10 M 3 11 A 2 2 0 0 0 5 13 L 11 13 A 2 2 0 0 0 13 11 L 13 5 A 2 2 0 0 0 11 3 L 11 3",
},
Shuffle: {
path: "M 5 2 L 2 5 L 5 8 M 2 5 L 12 5 M 11 14 L 14 11 L 11 8 M 14 11 L 4 11",
},
Undo: {
path: "M 5 3 L 2 6 L 5 9 M 2 6 L 10 6 A 3 3 0 1 1 10 13 L 8 13",
},
};
72 changes: 72 additions & 0 deletions src/components/MinimizeableModal.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<script lang="ts">
import Icon from "./Menu/Icon.svelte";
import Modal from "./Modal.svelte";

export let title: string;
export let position: "left" | "center" = "center";

let open = true;

const onClose = () => {
setTimeout(() => (open = false), 25);
};

const onOpen = () => {
open = true;
};
</script>

{#if open}
<Modal {title} {position} closeIcon="Minimize" on:close={onClose}>
<slot />
</Modal>
{/if}
<button class="minimized-modal {open ? '' : 'open'}" on:click={onOpen}>
<Icon icon="OpenModal" />
<span class="title">{title}</span>
</button>

<style lang="sass">
.minimized-modal
position: fixed
bottom: -1rem
right: 1.5rem
z-index: 2

appearance: none
background: white
border: 0px none
border-radius: 0.25em
cursor: pointer
padding: 0.33em 0.66em 1.33rem 0.5em

display: flex
align-items: center
gap: 0.25em
font-size: 1rem

box-shadow: 0 0 1rem rgba(0, 0, 0, 0.75)

transition: transform 100ms, box-shadow 100ms

transform: translateY(110%)

&.open
transform: translateY(0)

&:hover
transform: translateY(-0.15rem)

&:active
box-shadow: 0 -0.25rem 1rem rgba(0, 0, 0, 0.75)
transform: translateY(0.1rem)

&:focus-visible
outline: 4px solid black
outline-offset: 4px

:global(svg)
height: 1.5em
vertical-align: top
margin-right: 0.25em
</style>
6 changes: 4 additions & 2 deletions src/components/Modal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import { randomString } from "../utils/components";

import IconButton from "./Menu/IconButton.svelte";
import type { IIconName } from "./Menu/icons";

export let title: string;
export let position: "left" | "center" = "center";
export let closeIcon: IIconName = "Close";

const id = `modal-${randomString()}`;
const titleId = `${id}-title`;
Expand Down Expand Up @@ -39,8 +41,8 @@
};

let windowControlsOnLeft =
(navigator as any).windowControlsOverlay?.visible &&

Check warning on line 44 in src/components/Modal.svelte

View workflow job for this annotation

GitHub Actions / Lint, test, and build

Unexpected any. Specify a different type
(navigator as any).windowControlsOverlay?.getTitlebarAreaRect().left > 0;

Check warning on line 45 in src/components/Modal.svelte

View workflow job for this annotation

GitHub Actions / Lint, test, and build

Unexpected any. Specify a different type
</script>

<div class="backdrop" class:open />
Expand All @@ -55,7 +57,7 @@
>
<div class="header">
<h2 id={titleId}>{title}</h2>
<IconButton icon="Close" label="Close" onClick={onClose} />
<IconButton icon={closeIcon} label="Close" onClick={onClose} />
</div>
<div class="content">
<slot />
Expand Down Expand Up @@ -138,7 +140,7 @@
top: calc(env(titlebar-area-height) + 1rem)

background: white
z-index: 4
z-index: 6

font-size: 1rem
overflow: hidden
Expand Down
15 changes: 4 additions & 11 deletions src/views/Pileon/CompletedModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { createEventDispatcher } from "svelte";

import IconButton from "../../components/Menu/IconButton.svelte";
import Modal from "../../components/Modal.svelte";
import MinimizeableModal from "../../components/MinimizeableModal.svelte";
import { isCompleted, type Piles } from "../../utils/pileon";
import { type IEvent } from "../../utils/statistics";

Expand All @@ -11,20 +11,13 @@
export let events: IEvent[];
export let piles: Piles;

let show = true;
$: completed = isCompleted(piles);
$: events, (show = true);

const dispatch = createEventDispatcher();
</script>

{#if completed && show}
<Modal
title="Completed"
on:close={() => {
show = false;
}}
>
{#if completed}
<MinimizeableModal title="Completed">
<StatisticsTable {events} />
<div class="actions">
<IconButton
Expand All @@ -33,7 +26,7 @@
onClick={() => dispatch("shuffle")}
/>
</div>
</Modal>
</MinimizeableModal>
{/if}

<style lang="sass">
Expand Down
15 changes: 4 additions & 11 deletions src/views/Pileon/DeadEndModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,20 @@
import { createEventDispatcher } from "svelte";

import IconButton from "../../components/Menu/IconButton.svelte";
import Modal from "../../components/Modal.svelte";
import MinimizeableModal from "../../components/MinimizeableModal.svelte";
import { isDeadEnd, type Piles } from "../../utils/pileon";
import { cardsToPrettyString } from "../../utils/text";

export let piles: Piles;

let show = true;
$: [deadEnd, loopCards] = isDeadEnd(piles);
$: loopCardsStr = cardsToPrettyString(loopCards);
$: piles, (show = true);

const dispatch = createEventDispatcher();
</script>

{#if deadEnd && show}
<Modal
title="Dead end"
on:close={() => {
show = false;
}}
>
{#if deadEnd}
<MinimizeableModal title="Dead end">
{#if deadEnd === "dead-end"}
<p>No more possible moves. Undo the last move or start a new game.</p>
{/if}
Expand All @@ -40,7 +33,7 @@
/>
<IconButton icon="Undo" label="Undo" onClick={() => dispatch("undo")} />
</div>
</Modal>
</MinimizeableModal>
{/if}

<style lang="sass">
Expand Down
2 changes: 2 additions & 0 deletions src/views/Pileon/Pileon.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
calculateDimensions,
fillerStacks,
calculateTableEmSize,
isCompleted,
} from "../../utils/pileon";
import { getStackDataTransfer, stackWidthEm } from "../../utils/stack";
import { newEvent } from "../../utils/statistics";
Expand Down Expand Up @@ -75,6 +76,7 @@

$: piles = pilesHistory[pilesHistory.length - 1];
$: donePiles = getDonePiles(piles);
$: isCompleted(piles) && events.push(newEvent("stop"));

let selected: [number, Card[]] = [undefined, []];

Expand Down
Loading