Align ScaleDropdown better with fluent base

This commit is contained in:
natoverse 2022-11-21 12:32:15 -08:00
Родитель 69920e6c01
Коммит 4639afaaa4
5 изменённых файлов: 86 добавлений и 84 удалений

Просмотреть файл

@ -2,21 +2,30 @@
* Copyright (c) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE file in the project.
*/
import type { IDropdownOption } from '@fluentui/react'
import type {
IDropdownOption,
IDropdownStyleProps,
IDropdownStyles,
IStyleFunctionOrObject,
} from '@fluentui/react'
import type {
ContinuousColorScaleFunction,
NominalColorScaleFunction,
} from '@thematic/core'
import { chooseScale } from '@thematic/core'
import { useThematic } from '@thematic/react'
import { useSize } from 'ahooks'
import merge from 'lodash-es/merge.js'
import type React from 'react'
import { useMemo } from 'react'
import type { ChipsProps } from '../ScaleDropdown.types.js'
import { selectColorPalette } from '../ScaleDropdown.utils.js'
import type { ChipsProps } from './ScaleDropdown.types.js'
import { selectColorPalette } from './ScaleDropdown.utils.js'
const DEFAULT_WIDTH = 200
const DEFAULT_HEIGHT = 32
const ITEM_LEFT_PADDING = 8 // default right padding in fluent item
const ITEM_BORDER_MODIFIER = 1 // accounts for transparent border on outer container
const CARET_PADDING = 28 // defined default in fluent dropdown is 28 (this also aligns with item right padding)
export const TEXT_WIDTH = 80 // TODO: adjust this based on font size/max measured
const LABEL_HEIGHT = 29 // defined default in fluent dropdown
@ -25,16 +34,26 @@ const LABEL_HEIGHT = 29 // defined default in fluent dropdown
// visually we'll keep it lowercase in this app for visual consistency
const TITLE_CASE = false
export function useDropdownStyles(
styles?: IStyleFunctionOrObject<IDropdownStyleProps, IDropdownStyles>,
): IStyleFunctionOrObject<IDropdownStyleProps, IDropdownStyles> {
return useMemo(
() =>
merge(
{
root: {
textAlign: 'left',
},
},
styles,
),
[styles],
)
}
export function usePaletteWidth(width: number): number {
// subtract space for the caret, pad, text, etc.
return (
width -
ITEM_BORDER_MODIFIER -
ITEM_LEFT_PADDING -
TEXT_WIDTH -
CARET_PADDING -
ITEM_BORDER_MODIFIER
)
return width - ITEM_LEFT_PADDING - TEXT_WIDTH - CARET_PADDING
}
export function usePaletteHeight(height: number, label?: string): number {
@ -43,19 +62,6 @@ export function usePaletteHeight(height: number, label?: string): number {
return root / 2
}
/**
* Provides style overrides for root dropdown container
* @returns
*/
export function useContainerStyle(): React.CSSProperties {
return useMemo(
() => ({
textAlign: 'left',
}),
[],
)
}
/**
* This provides unique style overrides for the dropdown items,
* NOT the title. The paddings here are to align the item
@ -66,7 +72,7 @@ export function useItemStyle(width: number): React.CSSProperties {
return useMemo(
() => ({
width: width - CARET_PADDING,
paddingLeft: ITEM_BORDER_MODIFIER,
paddingLeft: 0,
paddingRight: CARET_PADDING,
}),
[width],
@ -110,3 +116,32 @@ export function useScale(
export function usePaletteComponent(key: string): React.FC<ChipsProps> {
return useMemo(() => selectColorPalette(key), [key])
}
export interface Dimensions {
width: number
height: number
}
export function useSafeDimensions(
ref: React.RefObject<HTMLDivElement>,
): Dimensions {
const dimensions = useSize(ref)
return (
dimensions || {
width: DEFAULT_WIDTH,
height: DEFAULT_HEIGHT,
}
)
}
/**
* Retrieve a non-zero width/height, because collapsible panels can force invalid color arrays
* @param width
* @param height
*/
export function useSafeCollapseDimensions(
width: number,
height: number,
): [number, number] {
return [width <= 0 ? 1 : width, height <= 0 ? 1 : height]
}

Просмотреть файл

@ -7,35 +7,35 @@ import { Dropdown } from '@fluentui/react'
import type { FC } from 'react'
import { useCallback, useRef } from 'react'
import { useSafeDimensions } from './hooks/size.js'
import {
useContainerStyle,
useDropdownStyles,
useItemStyle,
usePaletteHeight,
usePaletteWidth,
useSafeDimensions,
useThematicScaleOptions,
} from './hooks/theme.js'
} from './ScaleDropdown.hooks.js'
import type { ScaleDropdownProps } from './ScaleDropdown.types.js'
import { ScaleDropdownItem } from './ScaleDropdownItem.js'
import { ScaleDropdownOption } from './ScaleDropdownOption.js'
/**
* Represents a Fluent dropdown of Thematic scale options.
* The scale names can be accompanied by a visual rendering of the scale colors.
* This bascially extends Dropdown, overriding the options and item rendering.
*/
export const ScaleDropdown: FC<ScaleDropdownProps> = props => {
export const ScaleDropdown: FC<ScaleDropdownProps> = ({ styles, ...props }) => {
const ref = useRef(null)
const { width, height } = useSafeDimensions(ref)
const _styles = useDropdownStyles(styles)
const paletteWidth = usePaletteWidth(width)
const paletteHeight = usePaletteHeight(height, props.label)
const containerStyle = useContainerStyle()
const itemStyle = useItemStyle(width)
const options = useThematicScaleOptions()
const handleRenderTitle = useCallback(
(options: IDropdownOption<any>[] | undefined) => {
const firstOption: IDropdownOption<any> = options![0]!
return (
<ScaleDropdownItem
<ScaleDropdownOption
paletteWidth={paletteWidth}
paletteHeight={paletteHeight}
option={firstOption!}
@ -48,7 +48,7 @@ export const ScaleDropdown: FC<ScaleDropdownProps> = props => {
const handleRenderOption = useCallback(
(option: IDropdownOption<any> | undefined) => {
return option ? (
<ScaleDropdownItem
<ScaleDropdownOption
key={`scale-dropdown-item-${option.key as string}`}
paletteWidth={paletteWidth}
paletteHeight={paletteHeight}
@ -61,13 +61,13 @@ export const ScaleDropdown: FC<ScaleDropdownProps> = props => {
)
return (
<div style={containerStyle} ref={ref}>
<Dropdown
{...props}
options={options}
onRenderTitle={handleRenderTitle}
onRenderOption={handleRenderOption}
/>
</div>
<Dropdown
onRenderTitle={handleRenderTitle}
onRenderOption={handleRenderOption}
{...props}
ref={ref}
styles={_styles}
options={options}
/>
)
}

Просмотреть файл

@ -10,7 +10,7 @@ import type {
export type ScaleDropdownProps = Omit<IDropdownProps, 'options'>
export interface ScaleDropdownItemProps {
export interface ScaleDropdownOptionProps {
option: IDropdownOption
paletteWidth: number
paletteHeight: number

Просмотреть файл

@ -5,11 +5,15 @@
import type { FC } from 'react'
import { useMemo } from 'react'
import { useSafeCollapseDimensions } from './hooks/size.js'
import { TEXT_WIDTH, usePaletteComponent, useScale } from './hooks/theme.js'
import type { ScaleDropdownItemProps } from './ScaleDropdown.types.js'
import {
TEXT_WIDTH,
usePaletteComponent,
useSafeCollapseDimensions,
useScale,
} from './ScaleDropdown.hooks.js'
import type { ScaleDropdownOptionProps } from './ScaleDropdown.types.js'
export const ScaleDropdownItem: FC<ScaleDropdownItemProps> = ({
export const ScaleDropdownOption: FC<ScaleDropdownOptionProps> = ({
option,
paletteWidth,
paletteHeight,

Просмотреть файл

@ -1,37 +0,0 @@
/*!
* Copyright (c) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE file in the project.
*/
import { useSize } from 'ahooks'
const DEFAULT_WIDTH = 200
const DEFAULT_HEIGHT = 32
export interface Dimensions {
width: number
height: number
}
export function useSafeDimensions(
ref: React.RefObject<HTMLDivElement>,
): Dimensions {
const dimensions = useSize(ref)
return (
dimensions || {
width: DEFAULT_WIDTH,
height: DEFAULT_HEIGHT,
}
)
}
/**
* Retrieve a non-zero width/height, because collapsible panels can force invalid color arrays
* @param width
* @param height
*/
export function useSafeCollapseDimensions(
width: number,
height: number,
): [number, number] {
return [width <= 0 ? 1 : width, height <= 0 ? 1 : height]
}