Align ScaleDropdown better with fluent base
This commit is contained in:
Родитель
69920e6c01
Коммит
4639afaaa4
|
@ -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]
|
||||
}
|
Загрузка…
Ссылка в новой задаче