Skip to content
This repository was archived by the owner on Feb 21, 2025. It is now read-only.

Commit

Permalink
links
Browse files Browse the repository at this point in the history
  • Loading branch information
Bluerberry committed Nov 19, 2024
1 parent 0ff2afe commit edbfe07
Show file tree
Hide file tree
Showing 12 changed files with 270 additions and 28 deletions.
5 changes: 3 additions & 2 deletions src/lib/components/Graph.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@
.graph
position: relative
width: 100%
height: 650px
height: 100%
svg
width: 100%
display: block
height: 100%
width: 100%
.zoom
position: absolute
Expand Down
4 changes: 3 additions & 1 deletion src/lib/components/GraphPreview.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
// Exports
export let graph: GraphController
export let exit_button: boolean
// Functions
export function show() {
Expand Down Expand Up @@ -119,6 +118,9 @@
border-radius: $border-radius
box-shadow: $shadow
:global(.graph)
height: 750px
.tabs
display: flex
Expand Down
13 changes: 7 additions & 6 deletions src/lib/scripts/controllers/DomainController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,15 +431,16 @@ class DomainController extends NodeController<DomainController> {
// Unassign graph, parents, children, and subjects
if (this._graph_id !== undefined)
this.graph.unassignDomain(this)
if (this._parent_ids !== undefined)
for (const parent of this.parents)
parent.unassignChild(this, false)
if (this._child_ids !== undefined)
for (const child of this.children)
child.unassignParent(this, false)
if (this._subject_ids !== undefined)
for (const subject of this.subjects)
subject.unassignDomain(this, false)
for (const relation of this.graph.domain_relations) {
if (relation.parent === this)
relation.parent = null
else if (relation.child === this)
relation.child = null
}


// Fix order of remaining domains
if (reorder_graph) {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/scripts/controllers/LinkController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class LinkController {
get url(): string {
if (this.validate().severity === Severity.error)
return ''
return `/app/course/${this.course.code}/${this.name}`
return `${settings.ROOT_URL}/app/course/${this.course.code}/${this.name}`
}

// --------------------> Assignments
Expand Down
22 changes: 22 additions & 0 deletions src/lib/scripts/helpers/LinkHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,26 @@ export async function getGraph(id: number, ...relations: GraphRelation[]): Promi
if (data.graph !== null)
return await GraphHelper.reduce(data.graph, ...relations)
return null
}

export async function getGraphFromCourseAndName(course_code: string, link_name: string, ...relations: GraphRelation[]): Promise<SerializedGraph> {
try {
var data = await prisma.link.findFirstOrThrow({
where: {
course: {
code: course_code
},
name: link_name
},
select: {
graph: true
}
})
} catch (error) {
return Promise.reject(error)
}

if (data.graph !== null)
return await GraphHelper.reduce(data.graph, ...relations)
return Promise.reject('No graph associated with this link')
}
2 changes: 2 additions & 0 deletions src/lib/scripts/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ type Milliseconds = number

// -------------------> General settings

export const ROOT_URL = 'localhost:5173'

export const MAX_PROGRAM_NAME_LENGTH: Scalar = 50

export const COURSE_CODE_REGEX: RegExp = /^[A-Za-z0-9]*$/
Expand Down
28 changes: 17 additions & 11 deletions src/lib/scripts/svg/GraphSVG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,16 @@ class GraphSVG {
}

set lecture(lecture: LectureController | null) {
if (this.svg === undefined)
throw new Error('Failed to set lecture: GraphSVG not attached to DOM')
if (this.lecture === lecture || this.state === SVGState.animating)
return

this._lecture = lecture

if (this.state === SVGState.detached)
return
if (this.svg === undefined)
throw new Error('Failed to set lecture: GraphSVG not attached to DOM')

// Validate lecture
if (!this.validateView(this.view)) {
this.state = SVGState.broken
Expand All @@ -224,6 +227,8 @@ class GraphSVG {
}
}

this.state = SVGState.lecture

// Update highlights
d3.select(this.svg)
.select('#content')
Expand Down Expand Up @@ -811,9 +816,10 @@ class GraphSVG {
if (this.state === SVGState.broken) {
this.state = SVGState.animating
this.moveCamera(bbx.x, bbx.y, bbx.k)
this.setContent(this.graph.subjects, this.graph.subject_relations, () => {
this.state = this.interactive ? SVGState.dynamic : SVGState.static
})
this.setBackground('subjects')
this.setContent(this.graph.subjects, this.graph.subject_relations)
this.restoreContent()
this.state = this.interactive ? SVGState.dynamic : SVGState.static

return
}
Expand Down Expand Up @@ -921,9 +927,9 @@ class GraphSVG {
this.state = SVGState.animating
this.setBackground('domains')
this.moveCamera(bbx.x, bbx.y, bbx.k)
this.setContent(this.graph.domains, this.graph.domain_relations, () => {
this.state = this.interactive ? SVGState.dynamic : SVGState.static
})
this.setContent(this.graph.domains, this.graph.domain_relations)
this.restoreContent()
this.state = this.interactive ? SVGState.dynamic : SVGState.static

return
}
Expand All @@ -949,9 +955,9 @@ class GraphSVG {
this.state = SVGState.animating
this.setBackground('subjects')
this.moveCamera(bbx.x, bbx.y, bbx.k)
this.setContent(this.graph.subjects, this.graph.subject_relations, () => {
this.state = this.interactive ? SVGState.dynamic : SVGState.static
})
this.setContent(this.graph.subjects, this.graph.subject_relations)
this.restoreContent()
this.state = this.interactive ? SVGState.dynamic : SVGState.static

return
}
Expand Down
28 changes: 28 additions & 0 deletions src/routes/app/course/[course]/[link]/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

// External dependencies
import type { PageServerLoad } from './$types'

// Internal dependencies
import {
GraphHelper,
LinkHelper
} from '$scripts/helpers'

// Load
export const load: PageServerLoad = async ({ params }) => {
const course_code = params.course
const link_name = params.link

// Get data from the database
const graph = await LinkHelper.getGraphFromCourseAndName(course_code, link_name, 'domains', 'subjects', 'lectures')

// Start data streams
const domains = GraphHelper.getDomains(graph.id, 'graph', 'parents', 'children', 'subjects')
.catch(error => { throw new Error(error) })
const subjects = GraphHelper.getSubjects(graph.id, 'graph', 'parents', 'children', 'domain', 'lectures')
.catch(error => { throw new Error(error) })
const lectures = GraphHelper.getLectures(graph.id, 'graph', 'subjects')
.catch(error => { throw new Error(error) })

return { graph, domains, subjects, lectures }
}
180 changes: 180 additions & 0 deletions src/routes/app/course/[course]/[link]/[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@

<script lang="ts">
// External dependencies
import type { PageData } from './$types'
import { page } from '$app/stores'
// Internal dependencies
import { GraphSVG } from '$scripts/svg'
import { validEditorView } from '$scripts/types'
import {
ControllerCache,
GraphController,
DomainController,
SubjectController,
LectureController
} from '$scripts/controllers'
// Components
import Dropdown from '$components/Dropdown.svelte'
import Loading from '$components/Loading.svelte'
import Button from '$components/Button.svelte'
import Graph from '$components/Graph.svelte'
// Functions
async function revive() {
// Await all promises
const [
awaited_graph,
awaited_domains,
awaited_subjects,
awaited_lectures
] = await Promise.all([
data.graph,
data.domains,
data.subjects,
data.lectures
])
// Revive graph
graph = GraphController.revive(cache, awaited_graph)
// Revive controllers into cache
awaited_domains.forEach(domain => DomainController.revive(cache, domain))
awaited_subjects.forEach(subject => SubjectController.revive(cache, subject))
awaited_lectures.forEach(lecture => LectureController.revive(cache, lecture))
// Create graphSVG
graphSVG = new GraphSVG(graph, false)
const lecture = cache.find(LectureController, editor_lecture)
if (lecture !== undefined)
graphSVG.lecture = lecture
if (validEditorView(editor_view)) {
graphSVG.view = editor_view
}
}
// Initialization
export let data: PageData
const cache = new ControllerCache()
let graph: GraphController
let graphSVG: GraphSVG
const search_params = $page.url.searchParams
let editor_view = search_params.get('view')
let editor_lecture = Number(search_params.get('lecture'))
</script>


<!-- Markup -->


{#await revive()}
<Loading />
{:then}
<div class="tabular">
<div class="tabs">
<button
class="tab"
class:active={graphSVG.view === 'domains'}
on:click={() => graphSVG.view = 'domains'}
> Domains </button>

<button
class="tab"
class:active={graphSVG.view === 'subjects'}
on:click={() => graphSVG.view = 'subjects'}
> Subjects </button>

<button
class="tab"
class:active={graphSVG.view === 'lectures'}
on:click={() => graphSVG.view = 'lectures'}
> Lectures </button>

<div class="toolbar">
<Dropdown
id="lecture"
placeholder="Select lecture"
bind:value={graphSVG.lecture}
options={graph.lecture_options}
/>

<Button on:click={() => graphSVG.findGraph()}>
Find Graph
</Button>
</div>
</div>

<Graph {graphSVG} />
</div>
{/await}


<!-- Styles -->


<style lang="sass">
@use "$styles/variables.sass" as *
@use "$styles/palette.sass" as *
.tabular
position: relative
width: 100vw
height: 100vh
background: $white
.tabs
position: absolute
z-index: 1
top: 0
display: flex
width: 100%
background: $light-gray
border-radius: calc($border-radius - 1px) calc($border-radius - 1px) 0 0
.tab
padding: ($card-thin-padding + $input-thin-padding) $card-thick-padding
border-color: $gray
border-style: solid
border-width: 0 0 1px 1px
border-radius: calc($border-radius - 1px) calc($border-radius - 1px) 0 0
&.active
background: $white
border-width: 0 1px 0 1px
& ~ .tab
border-width: 0 1px 1px 0
&:first-child
border-left: none !important
.toolbar
display: flex
flex-flow: row nowrap
align-items: center
justify-content: flex-end
gap: $form-small-gap
flex: 1
padding: 0 $card-thick-padding
border-bottom: 1px solid $gray
:global(.dropdown)
max-width: 20rem
margin-left: $form-medium-gap
</style>
3 changes: 0 additions & 3 deletions src/routes/app/course/[course]/overview/GraphRow.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// Internal dependencies
import { course } from './stores'
import { GraphSVG } from '$scripts/svg'
import { FormModal, SimpleModal } from '$scripts/modals'
import { Validation, Severity } from '$scripts/validation'
Expand Down Expand Up @@ -87,8 +86,6 @@
export let graph: GraphController
// Variables
const graphSVG = new GraphSVG(graph, false)
let delete_modal = new DeleteModal()
let copy_modal = new CopyModal()
let preview_modal: GraphPreview
Expand Down
Loading

0 comments on commit edbfe07

Please sign in to comment.