Skip to content

Commit

Permalink
feat: #6870 Ensure steps can be programmatically styled (#7101)
Browse files Browse the repository at this point in the history
* feat: #6870 Ensure steps can be programmatically styled based on parent and context

- Pull code from TabView.js and ensure it works correctly under steps
- Put in some initial styling for `step` and `label`
- TODO add typing for parent and context

* feat: #6870 Add typing to steps.d.ts

Do not implement child props at the moment
  • Loading branch information
gcko authored Sep 2, 2024
1 parent 0a2e186 commit 2244dbd
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 20 deletions.
19 changes: 13 additions & 6 deletions components/lib/passthrough/tailwind/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2109,12 +2109,19 @@ const Tailwind = {
'focus:outline-none focus:outline-offset-0 focus:shadow-[0_0_0_0.2rem_rgba(191,219,254,1)] dark:focus:shadow-[0_0_0_0.2rem_rgba(147,197,253,0.5)]'
)
},
step: {
className: classNames('flex items-center justify-center', 'text-gray-700 dark:text-white/80 border border-gray-300 dark:border-blue-900/40 bg-white dark:bg-gray-900 w-[2rem] h-[2rem] leading-2rem text-sm z-10 rounded-full')
},
label: {
className: classNames('block', 'whitespace-nowrap overflow-hidden overflow-ellipsis max-w-full', 'mt-2 text-gray-500 dark:text-white/60')
}
step: ({ parent, context }) => ({
className: classNames('flex items-center justify-center', 'text-gray-700 dark:text-white/80 border border-gray-300 dark:border-blue-900/40 bg-white dark:bg-gray-900 w-[2rem] h-[2rem] leading-2rem text-sm z-10 rounded-full', {
'bg-white': parent.state.activeIndex !== context.index, // unselected item.
'bg-blue-500': parent.state.activeIndex === context.index // Selected item.
})
}),
label: ({ parent, context }) => ({
className: classNames('block', 'whitespace-nowrap overflow-hidden overflow-ellipsis max-w-full', 'mt-2 text-gray-500 dark:text-white/60', {
'font-normal': parent.state.activeIndex !== context.index, // unselected item.
'font-bold': parent.state.activeIndex === context.index, // Selected item.
'text-gray-500/60': context.disabled
})
})
},
tabmenu: {
root: 'overflow-x-auto',
Expand Down
44 changes: 36 additions & 8 deletions components/lib/steps/Steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,44 @@ export const Steps = React.memo(
const props = StepsBase.getProps(inProps, context);

const [idState, setIdState] = React.useState(props.id);
const [activeIndexState, setActiveIndexState] = React.useState(props.activeIndex);
const elementRef = React.useRef(null);
const listRef = React.useRef(null);
const count = React.Children.count(props.children);

const { ptm, cx, isUnstyled } = StepsBase.setMetaData({
const metaData = {
props,
state: {
id: idState
id: idState,
activeIndex: activeIndexState
}
};

const { ptm, ptmo, cx, isUnstyled } = StepsBase.setMetaData({
...metaData
});

useHandleStyle(StepsBase.css.styles, isUnstyled, { name: 'steps' });

const getStepPT = (step, key, index) => {
const stepMetaData = {
// props: step.props,
parent: metaData,
context: {
index,
count,
first: index === 0,
last: index === count - 1,
active: index === activeIndexState,
disabled: getStepProp(step, 'disabled')
}
};

return mergeProps(ptm(`step.${key}`, { step: stepMetaData }), ptm(`steps.${key}`, { steps: stepMetaData }), ptm(`steps.${key}`, stepMetaData), ptmo(getStepProp(step, 'pt'), key, stepMetaData));
};

const getStepProp = (step, name) => StepsBase.getCProp(step, name);

const itemClick = (event, item, index) => {
if (props.readOnly || item.disabled) {
event.preventDefault();
Expand All @@ -47,6 +73,8 @@ export const Steps = React.memo(
});
}

setActiveIndexState(index);

if (!item.url) {
event.preventDefault();
event.stopPropagation();
Expand Down Expand Up @@ -163,15 +191,15 @@ export const Steps = React.memo(
}

const key = item.id || idState + '_' + index;
const active = index === props.activeIndex;
const disabled = item.disabled || (index !== props.activeIndex && props.readOnly);
const active = index === activeIndexState;
const disabled = item.disabled || (index !== activeIndexState && props.readOnly);

const iconClassName = classNames('p-menuitem-icon', item.icon);
const iconProps = mergeProps(
{
className: cx('icon', { item })
},
ptm('icon')
getStepPT(item, 'icon', index)
);

const icon = IconUtils.getJSXIcon(item.icon, { ...iconProps }, { props });
Expand All @@ -180,7 +208,7 @@ export const Steps = React.memo(
{
className: cx('label')
},
ptm('label')
getStepPT(item, 'label', index)
);

const label = item.label && <span {...labelProps}>{item.label}</span>;
Expand All @@ -189,7 +217,7 @@ export const Steps = React.memo(
{
className: cx('step')
},
ptm('step')
getStepPT(item, 'step', index)
);

const actionProps = mergeProps(
Expand All @@ -202,7 +230,7 @@ export const Steps = React.memo(
onKeyDown: (event) => onItemKeyDown(event, item, index),
onClick: (event) => itemClick(event, item, index)
},
ptm('action')
getStepPT(item, 'action', index)
);

let content = (
Expand Down
5 changes: 3 additions & 2 deletions components/lib/steps/StepsBase.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ComponentBase } from '../componentbase/ComponentBase';
import { classNames } from '../utils/Utils';
import { classNames, ObjectUtils } from '../utils/Utils';

const classes = {
icon: ({ item }) => classNames('p-menuitem-icon', item.icon),
Expand Down Expand Up @@ -84,5 +84,6 @@ export const StepsBase = ComponentBase.extend({
css: {
classes,
styles
}
},
getCProp: (step, name) => ObjectUtils.getComponentProp(step, name, StepsBase.defaultProps)
});
52 changes: 48 additions & 4 deletions components/lib/steps/steps.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,50 @@ import { PassThroughOptions } from '../passthrough';
import { PassThroughType } from '../utils/utils';

export declare type StepsPassThroughType<T> = PassThroughType<T, StepsThroughMethodOptions>;
export declare type StepPassThroughType<T> = PassThroughType<T, StepPassThroughMethodOptions>;

/**
* Custom passthrough(pt) option method.
*/
export interface StepPassThroughMethodOptions {
// props: StepsProps;
parent: StepsThroughMethodOptions;
context: StepContext;
}

/**
* Defines current inline context in Steps component.
*/
export interface StepContext {
/**
* Step index.
*/
index: number;
/**
* Total number of steps
*/
count: number;
/**
* Is this the first step?
* @defaultValue false
*/
first: boolean;
/**
* Is this the last step?
* @defaultValue false
*/
last: boolean;
/**
* Is this step currently selected.
* @defaultValue false
*/
selected: boolean;
/**
* Is this step currently disabled.
* @defaultValue false
*/
disabled: boolean;
}

/**
* Custom passthrough(pt) option method.
Expand Down Expand Up @@ -42,19 +86,19 @@ export interface StepsPassThroughOptions {
/**
* Uses to pass attributes to the action's DOM element.
*/
action?: StepsPassThroughType<React.HTMLAttributes<HTMLAnchorElement>>;
action?: StepPassThroughType<React.HTMLAttributes<HTMLAnchorElement>>;
/**
* Uses to pass attributes to the step's DOM element.
*/
step?: StepsPassThroughType<React.HTMLAttributes<HTMLSpanElement>>;
step?: StepPassThroughType<React.HTMLAttributes<HTMLSpanElement>>;
/**
* Uses to pass attributes to the label's DOM element.
*/
label?: StepsPassThroughType<React.HTMLAttributes<HTMLSpanElement>>;
label?: StepPassThroughType<React.HTMLAttributes<HTMLSpanElement>>;
/**
* Uses to pass attributes to the icon's DOM element.
*/
icon?: StepsPassThroughType<React.SVGProps<SVGSVGElement> | React.HTMLAttributes<HTMLSpanElement>>;
icon?: StepPassThroughType<React.SVGProps<SVGSVGElement> | React.HTMLAttributes<HTMLSpanElement>>;
/**
* Used to manage all lifecycle hooks
* @see {@link ComponentHooks}
Expand Down

0 comments on commit 2244dbd

Please sign in to comment.