Skip to content

Commit

Permalink
Merge pull request #129 from jaipaljadeja/fix/cairo-example-dropdown-…
Browse files Browse the repository at this point in the history
…darkmode

Refactor: Example Selector
  • Loading branch information
mazurroman authored Mar 25, 2024
2 parents 09f4340 + fd21740 commit ccb7b06
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 79 deletions.
80 changes: 6 additions & 74 deletions components/Editor/EditorControls.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { useMemo, useRef, useState } from 'react'
import { useRef } from 'react'

import { RiLinksLine, RiQuestionLine, RiFileCodeLine } from '@remixicon/react'
import { RiLinksLine, RiQuestionLine } from '@remixicon/react'
import cn from 'classnames'
import { Priority, useRegisterActions } from 'kbar'
import { OnChangeValue } from 'react-select'

import examples from 'components/Editor/examples'
import { Button, Input } from 'components/ui'

import ExampleSelector from './ExampleSelector'

type SelectOption = {
value: number
label: string
Expand All @@ -17,7 +18,6 @@ type EditorControlsProps = {
isCompileDisabled: boolean
programArguments: string
areProgramArgumentsValid: boolean
exampleName: number
handleChangeExampleOption: (
option: OnChangeValue<SelectOption, false>,
) => void
Expand All @@ -31,15 +31,13 @@ const EditorControls = ({
isCompileDisabled,
programArguments,
areProgramArgumentsValid,
exampleName,
handleChangeExampleOption,
onCopyPermalink,
onCompileRun,
onProgramArgumentsUpdate,
onShowArgumentsHelper,
}: EditorControlsProps) => {
const inputRef = useRef<HTMLInputElement>(null)
const [isExampleSelectorOpen, setIsExampleSelectorOpen] = useState(false)

const actions = [
{
Expand Down Expand Up @@ -70,27 +68,7 @@ const EditorControls = ({

useRegisterActions(actions, [onCompileRun, onCopyPermalink])

const CairoNameExamples = useMemo(
() => [
'Simple',
'Variables & mutability',
'Type casting',
'Control flow',
'Functions',
'Arrays',
'Dictionaries',
'Ownership',
],
[],
)

const examplesOptions = examples.Cairo.map((example, i) => ({
value: i,
label: CairoNameExamples[i],
}))

const onExampleSelectorItemClick = (option: SelectOption) => {
setIsExampleSelectorOpen(false)
const onExampleChange = (option: SelectOption | null) => {
handleChangeExampleOption(option)
}

Expand All @@ -108,53 +86,7 @@ const EditorControls = ({
<RiLinksLine size={16} />
</Button>

<div className="relative">
<Button
onClick={() => setIsExampleSelectorOpen((prev) => !prev)}
transparent
padded={false}
tooltip="Change Cairo code example"
tooltipId="change-cairo-example"
className={`p-2 text-indigo-500 hover:text-indigo-600 focus:outline-none ${cn(
{
'bg-indigo-100': isExampleSelectorOpen,
},
)}`}
>
<RiFileCodeLine size={16} />
</Button>

{isExampleSelectorOpen && (
<ul
className="absolute left-0 bottom-0 z-20 mb-10 w-72 border-0 border-slate-200 origin-top-right overflow-hidden rounded-md bg-white shadow-lg ring-1 ring-gray-200 focus:outline-none"
tabIndex={-1}
role="listbox"
aria-labelledby="listbox-label"
aria-activedescendant="listbox-option-0"
>
<li className="p-4 border-l-2 border-white text-2xs text-gray-400 uppercase dark:text-gray-600">
Cairo Examples
</li>
{examplesOptions.map((option, idx) => (
<li
key={option.value}
className="text-gray-900 dark:text-gray-200 border-l-2 border-white cursor-pointer select-none p-4 text-sm hover:bg-gray-50 dark:hover:bg-black-50 hover:border-indigo-500"
id={`listbox-option-${idx}`}
role="option"
aria-selected={option.value === exampleName}
onKeyDown={(e) =>
e.key === 'Enter' && onExampleSelectorItemClick(option)
}
onClick={() => onExampleSelectorItemClick(option)}
>
<div className="flex flex-col">
<p>{option.label}</p>
</div>
</li>
))}
</ul>
)}
</div>
<ExampleSelector onExampleChange={onExampleChange} />
</div>

<div className="flex flex-row grow gap-x-2 items-center justify-end">
Expand Down
87 changes: 87 additions & 0 deletions components/Editor/ExampleSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React, { useId } from 'react'

import { RiFileCodeLine } from '@remixicon/react'
import cn from 'classnames'
import Select, {
components,
DropdownIndicatorProps,
StylesConfig,
GroupBase,
} from 'react-select'

import { Button } from '../ui'

import { Examples, CairoExampleNames } from './examples'

type SelectOption = {
value: number
label: string
}

type Props = {
onExampleChange: (option: SelectOption | null) => void
}

const examplesOptions = Examples.Cairo.map((_, i) => ({
value: i,
label: CairoExampleNames[i],
}))

function ExampleSelector({ onExampleChange }: Props) {
return (
<Select
onChange={onExampleChange}
options={examplesOptions}
components={{
DropdownIndicator,
}}
controlShouldRenderValue={false}
classNamePrefix="select"
styles={reactSelectStyles}
menuPlacement="top"
isSearchable={false}
instanceId={useId()}
/>
)
}

export default ExampleSelector

const reactSelectStyles: StylesConfig<
SelectOption,
false,
GroupBase<SelectOption>
> = {
// as we dont want to show value field in react-select
// and just want to show an icon, but input field takes some space
// so we hide the valueContainer with css properties
// Question: Why not remove the ValueContainer completely from 'components' prop in react-select?
// Ans: react-select needs a valuefield
// as many features like click outside to close drop down etc depends on it
// so we just add display none (it doesn't hurt)
valueContainer: (styles) => ({
...styles,
width: '0',
opacity: 0,
padding: '0 !important',
}),
}

const DropdownIndicator = (props: DropdownIndicatorProps<SelectOption>) => {
return (
<components.DropdownIndicator {...props}>
<Button
transparent
padded={false}
tooltip="Cairo Examples"
tooltipId="cairo-examples"
className={cn(
'p-2 text-indigo-500 hover:text-indigo-600 focus:outline-none',
props.selectProps.menuIsOpen ? 'bg-black-900/5 dark:bg-white/5' : '',
)}
>
<RiFileCodeLine size={16} />
</Button>
</components.DropdownIndicator>
)
}
13 changes: 11 additions & 2 deletions components/Editor/examples.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ExampleCode } from './types'

const examples: ExampleCode = {
export const Examples: ExampleCode = {
Cairo: [
`use core::felt252;
Expand Down Expand Up @@ -410,4 +410,13 @@ ret;
// ],
}

export default examples
export const CairoExampleNames = [
'Simple',
'Variables & mutability',
'Type casting',
'Control flow',
'Functions',
'Arrays',
'Dictionaries',
'Ownership',
]
5 changes: 2 additions & 3 deletions components/Editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { getAbsoluteURL } from 'util/browser'
import { isArgumentStringValid } from 'util/compiler'
import { codeHighlight, isEmpty, objToQueryString } from 'util/string'

import examples from 'components/Editor/examples'
import { Examples } from 'components/Editor/examples'
import { Tracer } from 'components/Tracer'

import { AppUiContext, CodeType, LogType } from '../../context/appUiContext'
Expand Down Expand Up @@ -92,7 +92,7 @@ const Editor = ({ readOnly = false }: Props) => {
getSetting(Setting.EditorCodeType) || CodeType.Cairo

setCodeType(initialCodeType)
setCairoCode(examples[initialCodeType][exampleOption])
setCairoCode(Examples[initialCodeType][exampleOption])
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [settingsLoaded && router.isReady, exampleOption])
Expand Down Expand Up @@ -393,7 +393,6 @@ const Editor = ({ readOnly = false }: Props) => {
onProgramArgumentsUpdate={handleProgramArgumentsUpdate}
onCompileRun={handleCompileRun}
onShowArgumentsHelper={() => setShowArgumentsHelper(true)}
exampleName={exampleOption}
handleChangeExampleOption={(newExample) =>
newExample !== null
? setExampleOption(newExample.value)
Expand Down

0 comments on commit ccb7b06

Please sign in to comment.