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

Support graph options #1

Merged
merged 8 commits into from
Apr 3, 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
4 changes: 4 additions & 0 deletions .github/workflows/ci_frontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
- name: Set up frontend
uses: actions/setup-node@v4
with:
Expand All @@ -26,6 +28,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
- name: Set up frontend
uses: actions/setup-node@v4
with:
Expand Down
4 changes: 2 additions & 2 deletions exe/diver_down_web
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ begin
# Rack 2.0
require 'rack'
require 'rack/server'
Rack::Server.new(app:).start
Rack::Server.new(app:, server: :webrick).start
rescue LoadError
# Rack 3.0
require 'rackup'
Rackup::Server.new(app:).start
Rackup::Server.new(app:, server: :webrick).start
end
19 changes: 16 additions & 3 deletions frontend/pages/Home/Show.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,24 @@ import { Loading } from '@/components/Loading'
import { Aside, Section, Sidebar, Stack } from '@/components/ui'
import { color } from '@/constants/theme'
import { useBitIdHash } from '@/hooks/useBitIdHash'
import { useLocalStorage } from '@/hooks/useLocalStorage'
import { useCombinedDefinition } from '@/repositories/combinedDefinitionRepository'

import { DefinitionGraph } from './components/DefinitionGraph'
import { DefinitionGraph, GraphOptions } from './components/DefinitionGraph'
import { DefinitionList } from './components/DefinitionList'
import { DefinitionSources } from './components/DefinitionSources'

export const Show: React.FC = () => {
const [selectedDefinitionIds, setSelectedDefinitionIds] = useBitIdHash()
const { data: combinedDefinition, isLoading } = useCombinedDefinition(selectedDefinitionIds)
const [graphOptions, setGraphOptions] = useLocalStorage<GraphOptions>('HomeShow-GraphOptions', {
compound: false,
concentrate: false,
})
const { data: combinedDefinition, isLoading } = useCombinedDefinition(
selectedDefinitionIds,
graphOptions.compound,
graphOptions.concentrate,
)

return (
<Wrapper>
Expand All @@ -32,7 +41,11 @@ export const Show: React.FC = () => {
</CenterStack>
) : (
<StyledStack>
<DefinitionGraph combinedDefinition={combinedDefinition} />
<DefinitionGraph
combinedDefinition={combinedDefinition}
graphOptions={graphOptions}
setGraphOptions={setGraphOptions}
/>
<StyledDefinitionSources combinedDefinition={combinedDefinition} />
</StyledStack>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, { useCallback, useState } from 'react'
import styled from 'styled-components'

import { ActionDialog, CheckBox, FormControl, Section, Stack } from '@/components/ui'
import { spacing } from '@/constants/theme'

export type GraphOptions = {
compound: boolean
concentrate: boolean
}

type Props = {
isOpen: boolean
onClickClose: () => void
graphOptions: GraphOptions
setGraphOptions: React.Dispatch<React.SetStateAction<GraphOptions>>
}

export const ConfigureViewOptionsDialog: React.FC<Props> = ({ isOpen, onClickClose, graphOptions, setGraphOptions }) => {
const [temporaryViewOptions, setTemporaryViewOptions] = useState<GraphOptions>(graphOptions)

const handleDialogClose = () => {
onClickClose()

// reset
setTemporaryViewOptions(graphOptions)
}

const handleSubmit = () => {
setGraphOptions(temporaryViewOptions)
onClickClose()
}

const onChangeCompound = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
setTemporaryViewOptions((prev) => ({ ...prev, compound: event.target.checked }))
},
[setTemporaryViewOptions],
)

const onChangeConcentrate = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
setTemporaryViewOptions((prev) => ({ ...prev, concentrate: event.target.checked }))
},
[setTemporaryViewOptions],
)

return (
<ActionDialog
title="Configure Graph Options"
decorators={{ closeButtonLabel: () => 'Close' }}
actionText="Save"
actionTheme="primary"
isOpen={isOpen}
onClickAction={handleSubmit}
onClickClose={handleDialogClose}
onClickOverlay={handleDialogClose}
width={'500px'}
>
<WrapperSection>
<Stack gap={1.5}>
<Stack gap={1.5}>
<p>Configure graph settings.</p>

<Stack gap={1.5}>
<FormControl title="Clip the boundary" helpMessage="Clip the boundary of the module.">
<CheckBox name="compound" onChange={onChangeCompound} checked={temporaryViewOptions.compound} />
</FormControl>

<FormControl
title="Use edge concentrators"
helpMessage="This merges multiedges into a single edge and causes partially parallel edges to share part of their paths."
>
<CheckBox name="compound" onChange={onChangeConcentrate} checked={temporaryViewOptions.concentrate} />
</FormControl>
</Stack>
</Stack>
</Stack>
</WrapperSection>
</ActionDialog>
)
}

const WrapperSection = styled(Section)`
padding: ${spacing.XS};
`
30 changes: 27 additions & 3 deletions frontend/pages/Home/components/DefinitionGraph/DefinitionGraph.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import { FC, useEffect, useState } from 'react'
import { FC, useCallback, useEffect, useState } from 'react'
import styled from 'styled-components'

import { Heading, LineClamp, Section, Text } from '@/components/ui'
import { Button, FaGearIcon, Heading, LineClamp, Section, Text } from '@/components/ui'
import { color } from '@/constants/theme'
import { CombinedDefinition } from '@/models/combinedDefinition'
import { renderDot } from '@/utils/renderDot'

import { ConfigureViewOptionsDialog, GraphOptions } from './ConfigureGraphOptionsDialog'
import { ScrollableSvg } from './ScrollableSvg'

type Props = {
combinedDefinition: CombinedDefinition
graphOptions: GraphOptions
setGraphOptions: React.Dispatch<React.SetStateAction<GraphOptions>>
}

export const DefinitionGraph: FC<Props> = ({ combinedDefinition }) => {
type DialogType = 'configureViewOptionsDiaglog'

export const DefinitionGraph: FC<Props> = ({ combinedDefinition, graphOptions, setGraphOptions }) => {
const [visibleDialog, setVisibleDialog] = useState<DialogType | null>(null)
const [svg, setSvg] = useState<string>('')

useEffect(() => {
Expand All @@ -28,8 +34,18 @@ export const DefinitionGraph: FC<Props> = ({ combinedDefinition }) => {
loadSvg()
}, [combinedDefinition.dot, setSvg])

const onClickCloseDialog = useCallback(() => {
setVisibleDialog(null)
}, [setVisibleDialog])

return (
<WrapperSection>
<ConfigureViewOptionsDialog
isOpen={visibleDialog === 'configureViewOptionsDiaglog'}
onClickClose={onClickCloseDialog}
graphOptions={graphOptions}
setGraphOptions={setGraphOptions}
/>
<FixedHeightHeading type="sectionTitle">
<LineClamp>
{combinedDefinition.titles.map((title, index) => (
Expand All @@ -38,6 +54,14 @@ export const DefinitionGraph: FC<Props> = ({ combinedDefinition }) => {
</BlockText>
))}
</LineClamp>
<Button
size="s"
square
onClick={() => setVisibleDialog('configureViewOptionsDiaglog')}
prefix={<FaGearIcon alt="Open Options" />}
>
Open View Options
</Button>
</FixedHeightHeading>
<FlexHeightSvgWrapper>
<ScrollableSvg svg={svg} />
Expand Down
1 change: 1 addition & 0 deletions frontend/pages/Home/components/DefinitionGraph/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { DefinitionGraph } from './DefinitionGraph'
export type { GraphOptions } from './ConfigureGraphOptionsDialog'
11 changes: 9 additions & 2 deletions frontend/repositories/combinedDefinitionRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import useSWR from 'swr'
import { path } from '@/constants/path'
import { CombinedDefinition } from '@/models/combinedDefinition'
import { bitIdToIds } from '@/utils/bitId'
import { stringify } from '@/utils/queryString'

import { get } from './httpRequest'

Expand Down Expand Up @@ -34,8 +35,14 @@ const fetchDefinitionShow = async (requestPath: string): Promise<CombinedDefinit
}
}

export const useCombinedDefinition = (ids: number[]) => {
const requestPath = path.api.definitions.show(ids)
const toBooleanFlag = (value: boolean) => (value ? '1' : null)

export const useCombinedDefinition = (ids: number[], compound: boolean, concentrate: boolean) => {
const params = {
compound: toBooleanFlag(compound),
concentrate: toBooleanFlag(concentrate),
}
const requestPath = `${path.api.definitions.show(ids)}?${stringify(params)}`
const shouldFetch = ids.length > 0
const { data, isLoading } = useSWR(shouldFetch ? requestPath : null, fetchDefinitionShow)

Expand Down
4 changes: 3 additions & 1 deletion lib/diver_down/web.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ def call(env)
action.module(module_name)
in ['GET', %r{\A/api/definitions/(?<bit_id>\d+)\.json\z}]
bit_id = Regexp.last_match[:bit_id].to_i
action.combine_definitions(bit_id)
compound = request.params['compound'] == '1'
concentrate = request.params['concentrate'] == '1'
action.combine_definitions(bit_id, compound, concentrate)
in ['GET', %r{\A/api/sources/(?<source>.+)\.json\z}]
source = Regexp.last_match[:source]
action.source(source)
Expand Down
6 changes: 4 additions & 2 deletions lib/diver_down/web/action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,9 @@ def pid
# GET /api/definitions/:bit_id.json
#
# @param bit_id [Integer]
def combine_definitions(bit_id)
# @param compound [Boolean]
# @param concentrate [Boolean]
def combine_definitions(bit_id, compound, concentrate)
ids = DiverDown::Web::BitId.bit_id_to_ids(bit_id)

valid_ids = ids.select do
Expand All @@ -167,7 +169,7 @@ def combine_definitions(bit_id)
json(
titles:,
bit_id: DiverDown::Web::BitId.ids_to_bit_id(valid_ids).to_s,
dot: DiverDown::Web::DefinitionToDot.new(definition).to_s,
dot: DiverDown::Web::DefinitionToDot.new(definition, compound:, concentrate:).to_s,
sources: definition.sources.map do
{
source_name: _1.source_name,
Expand Down
Loading