Skip to content

Instantly share code, notes, and snippets.

@speedwheel
Created November 5, 2024 12:34
Show Gist options
  • Save speedwheel/4acd224111befe8cb4f426b6a3b05c23 to your computer and use it in GitHub Desktop.
Save speedwheel/4acd224111befe8cb4f426b6a3b05c23 to your computer and use it in GitHub Desktop.
<script lang="ts">
import { Modal, Datepicker } from 'flowbite-svelte';
import TaskNameInput from '../inputs/TaskNameInput.svelte';
import { priorities, PriorityColors } from '$lib/constants/priorities';
import type { DropdownOption } from '$lib/types/dropdowns';
import { labelsStore, projectsStore } from '$lib/stores/filtersStore';
import TaskDescriptionInput from '../inputs/TaskDescriptionInput.svelte';
import TaskPriority from '../inputs/TaskPriority.svelte';
import TaskFilter from '../inputs/TaskFilter.svelte';
import { Icon, PlayCircle, Tag, Hashtag } from 'svelte-hero-icons';
import { getAppState } from '$lib/states/appState.svelte';
import { watch } from 'runed';
import { getModalState } from '$lib/states/modalState.svelte';
import { goto } from '$app/navigation';
import { toTitleCase } from '$lib/utils/stringFormatter';
import { onlyDate } from '$lib/utils/dateFormatter';
let modalDataName: string | null = $state('');
let modalDataDescription: string | null = $state('');
let modalDataPriority: DropdownOption = $state(priorities[1]);
let modalDataLabel: DropdownOption | null = $state(null);
let modalDataProject: DropdownOption | null = $state(null);
let modalDataPanelDate: Date | null = $state(new Date());
let labelDropdownOptions: DropdownOption[] = $state([]);
let projectDropdownOptions: DropdownOption[] = $state([]);
// let onUpdateTask = $state((updates: Partial<Task>) => {
// appState.updateTask(modalState.dataEditTask.id, updates);
// });
const appState = getAppState();
const modalState = getModalState();
// Subscribe to the dropdownOptionsStore
labelsStore.dropdownOptionsStore.subscribe((options) => {
labelDropdownOptions = options;
});
projectsStore.dropdownOptionsStore.subscribe((options) => {
projectDropdownOptions = options;
});
// // Update the modalDataName and modalDataDescription when the modal is opened
// // We are using watch because we might refresh or open the modal via URL
watch(
[() => modalState.editTaskID, () => appState.tasks.isLoading],
([editTaskID, tasksIsLoading]) => {
if (!editTaskID || tasksIsLoading) return;
const t = appState.getTask(editTaskID);
if (!t) return;
modalDataName = t.name;
modalDataDescription = t.description;
modalDataPriority = {
label: toTitleCase(t.priority),
value: t.priority,
color: PriorityColors[t.priority]
};
if (t.label_id) {
const label = $state.snapshot(labelsStore.getEntityById(t.label_id));
if (label) {
modalDataLabel = {
label: label.name,
value: label.id,
color: label.color
};
}
}
if (t.project_id) {
const project = $state.snapshot(projectsStore.getEntityById(t.project_id));
if (project) {
modalDataProject = {
label: project.name,
value: project.id,
color: project.color
};
}
}
if (t.panel_date) {
modalDataPanelDate = new Date(t.panel_date);
}
}
);
watch(
[
() => modalDataName,
() => modalDataDescription,
() => modalDataPriority,
() => modalDataLabel,
() => modalDataProject,
() => modalDataPanelDate
],
(
[name, description, priority, label, project, panelDate],
[namePrev, descriptionPrev, priorityPrev, labelPrev, projectPrev, panelDatePrev]
) => {
if (namePrev === undefined || descriptionPrev === undefined) return;
if (name !== namePrev) {
appState.updateTask(modalState.editTaskID, { name: name });
} else if (description != descriptionPrev) {
appState.updateTask(modalState.editTaskID, { description: description });
}
if (priority !== priorityPrev) {
appState.updateTask(modalState.editTaskID, { priority: priority.value });
}
if (label !== labelPrev) {
appState.updateTask(modalState.editTaskID, { label_id: label?.value });
}
if (project !== projectPrev) {
appState.updateTask(modalState.editTaskID, { project_id: project?.value });
}
if (panelDate !== panelDatePrev) {
appState.updateTask(modalState.editTaskID, { panel_date: onlyDate(panelDate) });
}
}
);
function onClose() {
goto('/');
}
</script>
{#snippet iconLabel(option: DropdownOption)}
<Icon src={Tag} class={`size-4 text-${option.color}`} />
{/snippet}
{#snippet iconProject(option: DropdownOption)}
<Icon src={Hashtag} class={`size-4 text-${option.color}`} />
{/snippet}
{#if modalDataName}
<Modal
title="Edit task"
on:close={onClose}
bind:open={modalState.isOpenEditTask}
outsideclose
size="lg"
backdropClass="fixed inset-0 z-40 dark:bg-neutral-700 bg-opacity-50 dark:bg-opacity-70"
classBody="dark:bg-neutral-900 !p-0 "
classFooter="dark:bg-neutral-900"
classHeader="dark:bg-neutral-900 dark:text-neutral-100 [&_button:hover]:dark:!bg-neutral-700"
>
<div class="flex">
<div class="flex-1 p-4 md:p-5">
<TaskNameInput bind:value={modalDataName} delay={300} />
<TaskDescriptionInput bind:value={modalDataDescription} />
</div>
<div class="w-52 bg-neutral-800 p-4">
<button
type="button"
class="mb-6 flex w-full items-center justify-center gap-2 rounded-md bg-accent-purple-1 py-1 font-medium dark:text-white"
>
<Icon src={PlayCircle} mini class="size-5 text-white" />
Start</button
>
<div class="mb-4 border-b border-dashed border-b-neutral-700 pb-4">
<div class="mb-2 text-xs dark:text-neutral-400">Priority</div>
<TaskPriority bind:value={modalDataPriority} class="!w-full" showChevron={true} />
</div>
<div class="mb-4 border-b border-dashed border-b-neutral-700 pb-4">
<div class="mb-2 text-xs dark:text-neutral-400">Label</div>
<TaskFilter
value={modalDataLabel}
dropdownOptions={labelDropdownOptions}
placeholder="Labels"
icon={iconLabel}
class="!w-full"
showChevron={true}
type="label"
/>
</div>
<div class="mb-4 border-b border-dashed border-b-neutral-700 pb-4">
<div class="mb-2 text-xs dark:text-neutral-400">Project</div>
<TaskFilter
value={modalDataProject}
dropdownOptions={projectDropdownOptions}
placeholder="Projects"
icon={iconProject}
class="!w-full"
showChevron={true}
type="project"
/>
</div>
<div>
<div class="mb-2 text-xs dark:text-neutral-400">Date</div>
<div class="tickup-datepicker [&_input]:!w-full">
<Datepicker
bind:value={modalDataPanelDate}
dateFormat={{ year: '2-digit', month: 'short', day: '2-digit' }}
defaultDate={new Date()}
autohide={true}
showActionButtons
/>
</div>
</div>
</div>
</div>
</Modal>
{/if}
<script lang="ts">
import type { DropdownOption } from '$lib/types/misc';
import CheckOutline from 'flowbite-svelte-icons/CheckOutline.svelte';
import type { Snippet } from 'svelte';
import { Icon, XMark, ChevronDown } from 'svelte-hero-icons';
import Select from 'svelte-select';
interface Props {
value: DropdownOption | null;
dropdownOptions: DropdownOption[];
placeholder: string;
icon: Snippet<[any]>;
class?: string;
showChevron?: boolean;
}
let { value = $bindable(), dropdownOptions, placeholder, icon, ...props }: Props = $props();
let floatingConfig = {
strategy: 'fixed'
};
</script>
<Select
on:clear={() => {
setTimeout(() => {
//newTask.priority = priorities[1];
}, 0);
}}
items={dropdownOptions}
{floatingConfig}
clearable={true}
{placeholder}
searchable={true}
bind:value
class={`tickup-select ${props.class}`}
showChevron={props.showChevron}
--height="32px"
>
<div class={`mr-1 text-${value?.color}`} slot="prepend">{@render icon?.('neutral-400')}</div>
<div slot="item" let:item>
{@render icon?.(item)}
{item.label}
{#if item.value == value?.value}
<CheckOutline class="ml-2 h-5 w-5 dark:text-accent-purple-2" />
{/if}
</div>
<div slot="selection" let:selection>
<!-- {@render icon?.(selection)} -->
{selection.label}
</div>
<div slot="clear-icon">
<Icon src={XMark} />
</div>
</Select>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment