Skip to content

Commit

Permalink
Subjects select rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
arackaf committed Dec 7, 2022
1 parent d617225 commit be81359
Show file tree
Hide file tree
Showing 13 changed files with 179 additions and 87 deletions.
2 changes: 1 addition & 1 deletion svelte-kit/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 2 additions & 35 deletions svelte-kit/src/data/subjects.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,8 @@
import { getDbConnection } from "./dbUtils";
import type { Subject } from "./types";

const { db } = await getDbConnection();

type Subject = {
_id: string;
name: string;
path: string;
};

type FullSubject = Subject & {
children: Subject[];
childLevel: number;
};

type SubjectHash = {
[_id: string]: Subject;
};

const stackAndGetTopLevelSubjects = (rawSubjects: Subject[]): FullSubject[] => {
//let subjects = Object.keys(subjectsHash).map(_id => ({ ...subjectsHash[_id] }));
const subjects: FullSubject[] = rawSubjects.map(s => ({
...s,
childLevel: 0,
children: []
}));

subjects.forEach(parent => {
parent.children.push(...subjects.filter(child => new RegExp(`,${parent._id},$`).test(child.path)));
parent.childLevel = !parent.path ? 0 : (parent.path.match(/\,/g) || []).length - 1;
});

return subjects.filter(s => s.path == null);
};

export const allSubjects = async () => {
const nativeStart = +new Date();

Expand All @@ -46,10 +16,7 @@ export const allSubjects = async () => {
console.log("Native time subjects", nativeEnd - nativeStart);
const allSubjectsSorted = result.map(s => ({ ...s, _id: s._id.toString() }));

const stackedSubjects = stackAndGetTopLevelSubjects(allSubjectsSorted);

return {
allSubjectsSorted,
stackedSubjects
allSubjectsSorted
};
};
19 changes: 19 additions & 0 deletions svelte-kit/src/data/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
export type Subject = {
_id: string;
name: string;
path: string;
};

export type SubjectWithPrepends = Subject & {
prepend: Subject[];
};

export type FullSubject = Subject & {
children: FullSubject[];
childLevel: number;
};

export type SubjectHash = {
[_id: string]: Subject;
};

export type Tag = {
_id: string;
name: string;
Expand Down
5 changes: 3 additions & 2 deletions svelte-kit/src/lib/components/editBook/EditBook.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import type { Tag } from "$data/types";
import type { Subject, Tag } from "$data/types";
import { Tabs, TabHeaders, TabHeader, TabContents, TabContent } from "../layout/tabs/index";
//import EditBookCovers from "./EditBookCovers.svelte";
Expand All @@ -8,6 +8,7 @@
export let book: any;
export let cancel: any;
export let subjects: Subject[];
export let tags: Tag[];
const updateBook = (fn: any) => (book = fn(book));
Expand All @@ -21,7 +22,7 @@
<TabContents>
<TabContent tabName="basic">
{#if book}
<EditBookInfo {book} {cancel} {tags} />
<EditBookInfo {book} {cancel} {subjects} {tags} />
{/if}
</TabContent>
<TabContent tabName="covers">
Expand Down
25 changes: 13 additions & 12 deletions svelte-kit/src/lib/components/editBook/EditBookInfo.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<script lang="ts">
import { enhance } from "$app/forms";
import { invalidate } from "$app/navigation";
import type { Tag } from "$data/types";
import type { Subject, Tag } from "$data/types";
import ActionButton from "../buttons/ActionButton.svelte";
import Button from "../buttons/Button.svelte";
import SelectAvailableTags from "$lib/components/subjectsAndTags/tags/SelectAvailableTags.svelte";
//import SelectAvailableSubjects from "app/components/subjectsAndTags/subjects/SelectAvailableSubjects.svelte";
import SelectAvailableSubjects from "$lib/components/subjectsAndTags/subjects/SelectAvailableSubjects.svelte";
import DisplaySelectedTags from "$lib/components/subjectsAndTags/tags/DisplaySelectedTags.svelte";
//import DisplaySelectedSubjects from "app/components/subjectsAndTags/subjects/DisplaySelectedSubjects.svelte";
Expand All @@ -16,6 +16,7 @@
export let book: any;
export let tags: Tag[];
export let subjects: Subject[];
let editingBook: any;
$: bookChanged(book);
Expand Down Expand Up @@ -115,16 +116,16 @@
</FlexRow>
</div>

<!-- <div class="col-xs-12">
<FlexRow>
<div class="col-sm-3 col-xs-12">
<SelectAvailableSubjects currentlySelected={editingBook.subjects} onSelect={addSubject} />
</div>
<div style="display: {editingBook.subjects.length ? '' : 'none'}" class="col-sm-9 col-xs-12">
<DisplaySelectedSubjects currentlySelected={editingBook.subjects} onRemove={removeSubject} />
</div>
</FlexRow>
</div> -->
<div class="col-xs-12">
<FlexRow>
<div class="col-sm-3 col-xs-12">
<SelectAvailableSubjects {subjects} currentlySelected={editingBook.subjects} onSelect={addSubject} />
</div>
<div style="display: {editingBook.subjects.length ? '' : 'none'}" class="col-sm-9 col-xs-12">
<!-- <DisplaySelectedSubjects currentlySelected={editingBook.subjects} onRemove={removeSubject} /> -->
</div>
</FlexRow>
</div>

{#each editingBook.authors || [] as author, index (index)}
<div class="col-xs-4">
Expand Down
5 changes: 3 additions & 2 deletions svelte-kit/src/lib/components/editBook/EditBookModal.svelte
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
<script lang="ts">
import type { Tag } from "$data/types";
import type { Subject, Tag } from "$data/types";
import Modal from "../ui/Modal.svelte";
import EditBook from "./EditBook.svelte";
export let book: any;
export let isOpen = false;
export let onHide: any;
export let tags: Tag[];
export let subjects: Subject[];
let closeModal: any;
</script>

<Modal headerCaption={`Edit: ${book.title}`} deferStateChangeOnClose={true} {isOpen} {onHide} standardFooter={false} bind:closeModal>
<EditBook {book} {tags} cancel={closeModal} />
<EditBook {book} {subjects} {tags} cancel={closeModal} />
</Modal>
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<script lang="ts">
import type { HierarchicalLabel } from "./types";
import LabelDisplay from "./LabelDisplay.svelte";
export let item;
export let item: HierarchicalLabel;
let effectiveChildLevel = item.childLevel - (item.prepend || []).length || 0;
</script>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<script lang="ts">
export let onClick = null;
export let item;
import type { Label } from "./types";
export let onClick: ((item: Label) => void) | null = null;
export let item: Label;
export let extraStyles = "";
export let disabled = false;
let className = "";
Expand All @@ -16,11 +18,14 @@
</script>

<span
on:click={onClick ? () => onClick(item) : e => (e.cancelBubble = false)}
on:click={onClick ? () => onClick?.(item) : e => (e.cancelBubble = false)}
on:keypress={() => {}}
style="cursor: {onClick ? 'pointer' : 'default'}; background-color: {item.backgroundColor}; color: {item.textColor || 'white'}; {stylesToAdd}"
class={"label label-default noselect " + extraClasses}
>
{#if $$slots.default}
<slot />
{:else}{item.name}{/if}
{:else}
{item.name}
{/if}
</span>
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
<script lang="ts">
import { stackedSubjects, filterSubjects, subjectsState } from "app/state/subjectsState";
import type { Subject } from "$data/types";
import { filterSubjects, subjectState } from "$lib/state/subjectsState";
//import { stackedSubjects, filterSubjects, subjectsState } from "app/state/subjectsState";
import GenericLabelSelect from "../GenericLabelSelect.svelte";
import type { Label } from "../types";
export let onSelect;
export let onSelect: (item: Label) => void;
export let placeholder = "Subjects";
export let currentlySelected = [];
export let currentlySelected: string[] = [];
export let subjects: Subject[];
$: ({ subjectsUnwound } = $stackedSubjects);
$: ({ subjectHash } = $subjectsState);
console.log(subjects);
//$: ({ subjectsUnwound } = $stackedSubjects);
//$: ({ subjectHash } = $subjectsState);
let search = "";
const doSelect = item => {
const doSelect = (item: Label) => {
onSelect(item);
search = "";
};
$: itemHash = currentlySelected.reduce((hash, _idOrObj) => ((hash[_idOrObj] = true), hash), {});
type LookupHash = { [_id: string]: true };
$: itemHash = currentlySelected.reduce<LookupHash>((hash, _idOrObj) => ((hash[_idOrObj] = true), hash), {});
$: subjectsPacket = subjectState(subjects);
$: eligible = filterSubjects(subjectsUnwound, search, subjectHash, itemHash);
$: eligible = filterSubjects(subjectsPacket.subjectsUnwound, search, subjectsPacket.subjectHash, itemHash);
</script>

<GenericLabelSelect {placeholder} bind:search options={eligible} onItemSelected={doSelect} />
10 changes: 10 additions & 0 deletions svelte-kit/src/lib/components/subjectsAndTags/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export type Label = {
name: string;
textColor: string;
backgroundColor: string;
};

export type HierarchicalLabel = Label & {
childLevel: number;
prepend: Label[];
};
96 changes: 96 additions & 0 deletions svelte-kit/src/lib/state/subjectsState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import type { FullSubject, Subject, SubjectHash, SubjectWithPrepends } from "$data/types";

export const subjectState = (allSubjectsSorted: Subject[] = []) => {
const subjects = stackAndGetTopLevelSubjects(allSubjectsSorted);
const subjectsUnwound = unwindSubjects(subjects);
const subjectHash = toHash(allSubjectsSorted);

return {
subjects,
subjectsUnwound,
subjectHash
};
};

export const stackAndGetTopLevelSubjects = (allSubjects: Subject[]): FullSubject[] => {
//let subjects = Object.keys(subjectsHash).map(_id => ({ ...subjectsHash[_id] }));
const subjects: FullSubject[] = allSubjects.map(s => ({
...s,
childLevel: 0,
children: []
}));

subjects.forEach(parent => {
parent.children.push(...subjects.filter(child => new RegExp(`,${parent._id},$`).test(child.path)));
parent.childLevel = !parent.path ? 0 : (parent.path.match(/\,/g) || []).length - 1;
});

return subjects.filter(s => s.path == null);
};

export const unwindSubjects = (subjects: FullSubject[]): FullSubject[] => {
let result: FullSubject[] = [];
subjects.forEach(s => result.push(s, ...unwindSubjects(s.children || [])));
return result;
};

export const toHash = (subjects: Subject[]): SubjectHash => {
return subjects.reduce<SubjectHash>((hash, tag) => {
hash[tag._id] = tag;
return hash;
}, {});
};

type LookupHash = { [_id: string]: true };
type SearchFn = (s: Subject) => boolean;

export const filterSubjects = (subjects: Subject[], search?: string, lookupMap: SubjectHash = {}, alreadySelected: LookupHash = {}) => {
let searchFn: SearchFn;
if (!search) {
searchFn = s => !alreadySelected[s._id];
} else {
let regex = new RegExp(search, "i");
searchFn = s => regex.test(s.name) && !alreadySelected[s._id];
}
return subjects.reduce<SubjectWithPrepends[]>((result, s) => {
if (searchFn(s)) {
const entry: SubjectWithPrepends = { ...s, prepend: [] };

let currentSubject = s;
let parentId;
let ancestorsInactive = 0;
while ((parentId = computeSubjectParentId(currentSubject.path))) {
if (!parentId) {
break;
}
let parent = lookupMap[parentId];
if (!parent) {
break;
}

if (alreadySelected[parent._id] || !searchFn(parent)) {
ancestorsInactive++;
}

entry.prepend.unshift(parent);
currentSubject = parent;
}

if (!ancestorsInactive) {
entry.prepend = [];
}

result.push(entry);
}
return result;
}, []);
};

const computeSubjectParentId = (path: string) => {
if (path) {
let pathParts = path.split(",");
return pathParts[pathParts.length - 2];
} else {
return "";
}
};
13 changes: 0 additions & 13 deletions svelte-kit/src/lib/state/tagsState.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
import type { Tag, TagHash } from "$data/types";

// export const tagsState = derived(queryState, $tags => {
// const tags = $tags.data ? $tags.data.allTags.Tags : [];
// const tagHash = tags?.length ? tags.reduce((hash, t) => ((hash[t._id] = t), hash), {}) : {};

// return { tagsLoaded: $tags.loaded, tags, tagHash };
// });

// function tagsSort({ name: name1 }, { name: name2 }) {
// let name1After = name1.toLowerCase() > name2.toLowerCase();
// let bothEqual = name1.toLowerCase() === name2.toLowerCase();
// return bothEqual ? 0 : name1After ? 1 : -1;
// }

export const toHash = (tags: Tag[]): TagHash => {
return tags.reduce<TagHash>((hash, tag) => {
hash[tag._id] = tag;
Expand Down
Loading

1 comment on commit be81359

@vercel
Copy link

@vercel vercel bot commented on be81359 Dec 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.