Add a current default unit indicator with menu to switch (#2937)
* Add a units indicator with a menu to switch default units * Add a playwright test, add a SR label * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) * Re-run CI * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) * Re-run CI --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
@ -7134,6 +7134,40 @@ test.describe('Testing Gizmo', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Units menu', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await page.goto('/')
|
||||||
|
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
const unitsMenuButton = page.getByRole('button', {
|
||||||
|
name: 'Current Units',
|
||||||
|
exact: false,
|
||||||
|
})
|
||||||
|
await expect(unitsMenuButton).toBeVisible()
|
||||||
|
await expect(unitsMenuButton).toContainText('in')
|
||||||
|
|
||||||
|
await unitsMenuButton.click()
|
||||||
|
const millimetersButton = page.getByRole('button', { name: 'Millimeters' })
|
||||||
|
|
||||||
|
await expect(millimetersButton).toBeVisible()
|
||||||
|
await millimetersButton.click()
|
||||||
|
|
||||||
|
// Look out for the toast message
|
||||||
|
const toastMessage = page.getByText(
|
||||||
|
`Set default unit to "mm" for this project`
|
||||||
|
)
|
||||||
|
await expect(toastMessage).toBeVisible()
|
||||||
|
|
||||||
|
// Verify that the popover has closed
|
||||||
|
await expect(millimetersButton).not.toBeAttached()
|
||||||
|
|
||||||
|
// Verify that the button label has updated
|
||||||
|
await expect(unitsMenuButton).toContainText('mm')
|
||||||
|
})
|
||||||
|
|
||||||
test('Successful export shows a success toast', async ({ page }) => {
|
test('Successful export shows a success toast', async ({ page }) => {
|
||||||
// FYI this test doesn't work with only engine running locally
|
// FYI this test doesn't work with only engine running locally
|
||||||
// And you will need to have the KittyCAD CLI installed
|
// And you will need to have the KittyCAD CLI installed
|
||||||
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 29 KiB |
@ -25,6 +25,7 @@ import ModalContainer from 'react-modal-promise'
|
|||||||
import useHotkeyWrapper from 'lib/hotkeyWrapper'
|
import useHotkeyWrapper from 'lib/hotkeyWrapper'
|
||||||
import Gizmo from 'components/Gizmo'
|
import Gizmo from 'components/Gizmo'
|
||||||
import { CoreDumpManager } from 'lib/coredump'
|
import { CoreDumpManager } from 'lib/coredump'
|
||||||
|
import { UnitsMenu } from 'components/UnitsMenu'
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
useRefreshSettings(paths.FILE + 'SETTINGS')
|
useRefreshSettings(paths.FILE + 'SETTINGS')
|
||||||
@ -127,6 +128,7 @@ export function App() {
|
|||||||
<Stream />
|
<Stream />
|
||||||
{/* <CamToggle /> */}
|
{/* <CamToggle /> */}
|
||||||
<LowerRightControls coreDumpManager={coreDumpManager}>
|
<LowerRightControls coreDumpManager={coreDumpManager}>
|
||||||
|
<UnitsMenu />
|
||||||
<Gizmo />
|
<Gizmo />
|
||||||
</LowerRightControls>
|
</LowerRightControls>
|
||||||
</div>
|
</div>
|
||||||
|
@ -119,7 +119,7 @@ export default function Gizmo() {
|
|||||||
<div
|
<div
|
||||||
ref={wrapperRef}
|
ref={wrapperRef}
|
||||||
aria-label="View orientation gizmo"
|
aria-label="View orientation gizmo"
|
||||||
className="grid place-content-center rounded-full overflow-hidden border border-solid border-primary/50 pointer-events-auto"
|
className="grid place-content-center rounded-full overflow-hidden border border-solid border-primary/50 pointer-events-auto bg-chalkboard-10/70 dark:bg-chalkboard-100/80 backdrop-blur-sm"
|
||||||
>
|
>
|
||||||
<canvas ref={canvasRef} />
|
<canvas ref={canvasRef} />
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
|
54
src/components/UnitsMenu.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { Popover } from '@headlessui/react'
|
||||||
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
|
import { baseUnitLabels, baseUnitsUnion } from 'lib/settings/settingsTypes'
|
||||||
|
|
||||||
|
export function UnitsMenu() {
|
||||||
|
const { settings } = useSettingsAuthContext()
|
||||||
|
return (
|
||||||
|
<Popover className="relative pointer-events-auto">
|
||||||
|
{({ close }) => (
|
||||||
|
<>
|
||||||
|
<Popover.Button
|
||||||
|
className={`flex items-center gap-2 px-3 py-1
|
||||||
|
text-xs text-primary bg-chalkboard-10/70 dark:bg-chalkboard-100/80 backdrop-blur-sm
|
||||||
|
border !border-primary/50 rounded-full`}
|
||||||
|
>
|
||||||
|
<div className="w-4 h-[1px] bg-primary relative">
|
||||||
|
<div className="absolute w-[1px] h-[1em] bg-primary left-0 top-1/2 -translate-y-1/2"></div>
|
||||||
|
<div className="absolute w-[1px] h-[1em] bg-primary right-0 top-1/2 -translate-y-1/2"></div>
|
||||||
|
</div>
|
||||||
|
<span className="sr-only">Current units are: </span>
|
||||||
|
{settings.context.modeling.defaultUnit.current}
|
||||||
|
</Popover.Button>
|
||||||
|
<Popover.Panel
|
||||||
|
className={`absolute bottom-full right-0 mb-2 w-48 bg-chalkboard-10 dark:bg-chalkboard-90
|
||||||
|
border border-solid border-chalkboard-10 dark:border-chalkboard-90 rounded
|
||||||
|
shadow-lg`}
|
||||||
|
>
|
||||||
|
<ul className="relative flex flex-col gap-0.5 items-stretch content-stretch">
|
||||||
|
{baseUnitsUnion.map((unit) => (
|
||||||
|
<li key={unit} className="contents">
|
||||||
|
<button
|
||||||
|
className="flex items-center gap-2 py-1 px-2 cursor-pointer hover:bg-chalkboard-20 dark:hover:bg-chalkboard-80 border-none text-left"
|
||||||
|
onClick={() => {
|
||||||
|
settings.send({
|
||||||
|
type: 'set.modeling.defaultUnit',
|
||||||
|
data: {
|
||||||
|
level: 'project',
|
||||||
|
value: unit,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
close()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{baseUnitLabels[unit]}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</Popover.Panel>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Popover>
|
||||||
|
)
|
||||||
|
}
|
@ -16,6 +16,14 @@ export const baseUnits = {
|
|||||||
export type BaseUnit = Models['UnitLength_type']
|
export type BaseUnit = Models['UnitLength_type']
|
||||||
|
|
||||||
export const baseUnitsUnion = Object.values(baseUnits).flatMap((v) => v)
|
export const baseUnitsUnion = Object.values(baseUnits).flatMap((v) => v)
|
||||||
|
export const baseUnitLabels = {
|
||||||
|
in: 'Inches',
|
||||||
|
ft: 'Feet',
|
||||||
|
yd: 'Yards',
|
||||||
|
mm: 'Millimeters',
|
||||||
|
cm: 'Centimeters',
|
||||||
|
m: 'Meters',
|
||||||
|
} as const
|
||||||
|
|
||||||
export type Toggle = 'On' | 'Off'
|
export type Toggle = 'On' | 'Off'
|
||||||
export const toggleAsArray = ['On', 'Off'] as const
|
export const toggleAsArray = ['On', 'Off'] as const
|
||||||
|