Skip to content

Expose default classnames #47

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/components/DisabledItem.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useContext } from "react";

import { SelectContext } from "./SelectProvider";
import { DEFAULT_CLASSNAMES } from "../constants";

interface DisabledItemProps {
children: JSX.Element | string;
Expand All @@ -12,8 +13,8 @@ const DisabledItem: React.FC<DisabledItemProps> = ({ children }) => {
<div
className={
classNames && classNames.listDisabledItem
? classNames.listDisabledItem
: "px-2 py-2 cursor-not-allowed truncate text-gray-400 select-none"
? classNames.listDisabledItem(DEFAULT_CLASSNAMES.listDisabledItem)
: DEFAULT_CLASSNAMES.listDisabledItem
}
>
{children}
Expand Down
5 changes: 3 additions & 2 deletions src/components/GroupItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from "react";
import Item from "./Item";
import { useSelectContext } from "./SelectProvider";
import { GroupOption } from "./type";
import { DEFAULT_CLASSNAMES } from "../constants";

interface GroupItemProps {
item: GroupOption;
Expand All @@ -22,8 +23,8 @@ const GroupItem: React.FC<GroupItemProps> = ({ item, primaryColor }) => {
<div
className={
classNames?.listGroupLabel
? classNames.listGroupLabel
: "pr-2 py-2 cursor-default select-none truncate font-bold text-gray-700"
? classNames.listGroupLabel(DEFAULT_CLASSNAMES.listGroupLabel)
: DEFAULT_CLASSNAMES.listGroupLabel
}
>
{item.label}
Expand Down
12 changes: 5 additions & 7 deletions src/components/Item.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback, useMemo } from "react";

import { COLORS, DEFAULT_THEME, THEME_DATA } from "../constants";
import { COLORS, DEFAULT_CLASSNAMES, DEFAULT_THEME, THEME_DATA } from "../constants";

import DisabledItem from "./DisabledItem";
import { useSelectContext } from "./SelectProvider";
Expand Down Expand Up @@ -40,15 +40,13 @@ const Item: React.FC<ItemProps> = ({ item, primaryColor }) => {
}, [primaryColor]);

const getItemClass = useCallback(() => {
const baseClass =
"block transition duration-200 px-2 py-2 cursor-pointer select-none truncate rounded";
const selectedClass = isSelected
? `text-white ${bgColor}`
: `text-gray-500 ${bgHoverColor} ${textHoverColor}`;

return classNames && classNames.listItem
? classNames.listItem({ isSelected })
: `${baseClass} ${selectedClass}`;
? classNames.listItem(DEFAULT_CLASSNAMES.listItem, { isSelected })
: `${DEFAULT_CLASSNAMES.listItem} ${selectedClass}`;
}, [bgColor, bgHoverColor, classNames, isSelected, textHoverColor]);

return (
Expand All @@ -65,8 +63,8 @@ const Item: React.FC<ItemProps> = ({ item, primaryColor }) => {
<li
tabIndex={0}
onKeyDown={(e: React.KeyboardEvent<HTMLLIElement>) => {
if (e.key === ' ' || e.key === 'Enter') {
handleValueChange(item)
if (e.key === " " || e.key === "Enter") {
handleValueChange(item);
}
}}
aria-selected={isSelected}
Expand Down
8 changes: 6 additions & 2 deletions src/components/Options.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback, useContext, useMemo } from "react";

import { DEFAULT_THEME } from "../constants";
import { DEFAULT_CLASSNAMES, DEFAULT_THEME } from "../constants";

import DisabledItem from "./DisabledItem";
import GroupItem from "./GroupItem";
Expand Down Expand Up @@ -94,7 +94,11 @@ const Options: React.FC<OptionsProps> = ({
return (
<div
role="options"
className={classNames && classNames.list ? classNames.list : "max-h-72 overflow-y-auto"}
className={
classNames && classNames.list
? classNames.list(DEFAULT_CLASSNAMES.list)
: DEFAULT_CLASSNAMES.list
}
>
{filterResult.map((item, index) => (
<React.Fragment key={index}>
Expand Down
13 changes: 7 additions & 6 deletions src/components/SearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { forwardRef, useContext } from "react";

import { SearchIcon } from "./Icons";
import { SelectContext } from "./SelectProvider";
import { DEFAULT_CLASSNAMES } from "../constants";

interface SearchInputProps {
placeholder?: string;
Expand All @@ -19,23 +20,23 @@ const SearchInput = forwardRef<HTMLInputElement, SearchInputProps>(function Sear
<div
className={
classNames && classNames.searchContainer
? classNames.searchContainer
: "relative py-1 px-2.5"
? classNames.searchContainer(DEFAULT_CLASSNAMES.searchContainer)
: DEFAULT_CLASSNAMES.searchContainer
}
>
<SearchIcon
className={
classNames && classNames.searchIcon
? classNames.searchIcon
: "absolute w-5 h-5 mt-2.5 pb-0.5 ml-2 text-gray-500"
? classNames.searchIcon(DEFAULT_CLASSNAMES.searchIcon)
: DEFAULT_CLASSNAMES.searchIcon
}
/>
<input
ref={ref}
className={
classNames && classNames.searchBox
? classNames.searchBox
: "w-full py-2 pl-8 text-sm text-gray-500 bg-gray-100 border border-gray-200 rounded focus:border-gray-200 focus:ring-0 focus:outline-none"
? classNames.searchBox(DEFAULT_CLASSNAMES.searchBox)
: DEFAULT_CLASSNAMES.searchBox
}
type="text"
placeholder={placeholder}
Expand Down
39 changes: 21 additions & 18 deletions src/components/Select.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useRef, useState } from "react";

import { COLORS, DEFAULT_THEME, THEME_DATA } from "../constants";
import { COLORS, DEFAULT_CLASSNAMES, DEFAULT_THEME, THEME_DATA } from "../constants";
import useOnClickOutside from "../hooks/use-onclick-outside";

import { ChevronIcon, CloseIcon } from "./Icons";
Expand Down Expand Up @@ -142,26 +142,23 @@ const Select: React.FC<SelectProps> = ({
borderFocus =
THEME_DATA.borderFocus[primaryColor as keyof typeof THEME_DATA.borderFocus];
}
const baseClass =
"flex text-sm text-gray-500 border border-gray-300 rounded shadow-sm transition-all duration-300 focus:outline-none";
const defaultClass = `${baseClass} ${
const defaultClass = `${DEFAULT_CLASSNAMES.menuButton} ${
isDisabled
? "bg-gray-200"
: `bg-white hover:border-gray-400 ${borderFocus} focus:ring ${ringColor}`
}`;

return classNames && classNames.menuButton
? classNames.menuButton({ isDisabled })
? classNames.menuButton(DEFAULT_CLASSNAMES.menuButton, { isDisabled })
: defaultClass;
}, [classNames, isDisabled, primaryColor]);

const getTagItemClass = useCallback(
(item: Option) => {
const baseClasse = "bg-gray-200 border rounded-sm flex space-x-1";
const disabledClass = isDisabled ? "border-gray-500 px-1" : "pl-1";
return classNames?.tagItem
? classNames.tagItem({ item, isDisabled })
: `${baseClasse} ${disabledClass}`;
? classNames.tagItem(DEFAULT_CLASSNAMES.tagItem, { item, isDisabled })
: `${DEFAULT_CLASSNAMES.tagItem} ${disabledClass}`;
},
[classNames, isDisabled]
);
Expand Down Expand Up @@ -198,8 +195,10 @@ const Select: React.FC<SelectProps> = ({
<p
className={
classNames?.tagItemText
? classNames.tagItemText
: "text-gray-600 truncate cursor-default select-none"
? classNames.tagItemText(
DEFAULT_CLASSNAMES.tagItemText
)
: DEFAULT_CLASSNAMES.tagItemText
}
>
{item.label}
Expand All @@ -211,15 +210,19 @@ const Select: React.FC<SelectProps> = ({
onClick={e => removeItem(e, item)}
className={
classNames?.tagItemIconContainer
? classNames.tagItemIconContainer
: "flex items-center px-1 cursor-pointer rounded-r-sm hover:bg-red-200 hover:text-red-600"
? classNames.tagItemIconContainer(
DEFAULT_CLASSNAMES.tagItemIconContainer
)
: DEFAULT_CLASSNAMES.tagItemIconContainer
}
>
<CloseIcon
className={
classNames?.tagItemIcon
? classNames.tagItemIcon
: "w-3 h-3 mt-0.5"
? classNames.tagItemIcon(
DEFAULT_CLASSNAMES.tagItemIcon
)
: DEFAULT_CLASSNAMES.tagItemIcon
}
/>
</div>
Expand All @@ -242,8 +245,8 @@ const Select: React.FC<SelectProps> = ({
<CloseIcon
className={
classNames?.closeIcon
? classNames.closeIcon
: "w-5 h-5 p-0.5"
? classNames.closeIcon(DEFAULT_CLASSNAMES.closeIcon)
: DEFAULT_CLASSNAMES.closeIcon
}
/>
</div>
Expand All @@ -267,8 +270,8 @@ const Select: React.FC<SelectProps> = ({
<div
className={
classNames?.menu
? classNames.menu
: "absolute z-10 w-full bg-white shadow-lg border rounded py-1 mt-1.5 text-sm text-gray-700"
? classNames.menu(DEFAULT_CLASSNAMES.menu)
: DEFAULT_CLASSNAMES.menu
}
>
{isSearchable && (
Expand Down
28 changes: 14 additions & 14 deletions src/components/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,21 @@ export interface GroupOption {
export type Options = Array<Option | GroupOption>;

export interface ClassNames {
menuButton?: (value?: { isDisabled?: boolean }) => string;
menu?: string;
tagItem?: (value?: { item?: Option; isDisabled?: boolean }) => string;
tagItemText?: string;
tagItemIconContainer?: string;
tagItemIcon?: string;
list?: string;
listGroupLabel?: string;
listItem?: (value?: { isSelected?: boolean }) => string;
listDisabledItem?: string;
menuButton?: (defaultClassName: string, value?: { isDisabled?: boolean }) => string;
menu?: (defaultClassName: string) => string;
tagItem?: (defaultClassName: string, value?: { item?: Option; isDisabled?: boolean }) => string;
tagItemText?: (defaultClassName: string) => string;
tagItemIconContainer?: (defaultClassName: string) => string;
tagItemIcon?: (defaultClassName: string) => string;
list?: (defaultClassName: string) => string;
listGroupLabel?: (defaultClassName: string) => string;
listItem?: (defaultClassName: string, value?: { isSelected?: boolean }) => string;
listDisabledItem?: (defaultClassName: string) => string;
ChevronIcon?: (value?: { open?: boolean }) => string;
searchContainer?: string;
searchBox?: string;
searchIcon?: string;
closeIcon?: string;
searchContainer?: (defaultClassName: string) => string;
searchBox?: (defaultClassName: string) => string;
searchIcon?: (defaultClassName: string) => string;
closeIcon?: (defaultClassName: string) => string;
}

export type SelectValue = Option | Option[] | null;
Expand Down
20 changes: 20 additions & 0 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,26 @@ export const COLORS = [

export const DEFAULT_THEME = "blue";

export const DEFAULT_CLASSNAMES = {
searchContainer: "relative py-1 px-2.5",
searchIcon: "absolute w-5 h-5 mt-2.5 pb-0.5 ml-2 text-gray-500",
searchBox:
"w-full py-2 pl-8 text-sm text-gray-500 bg-gray-100 border border-gray-200 rounded focus:border-gray-200 focus:ring-0 focus:outline-none",
listDisabledItem: "px-2 py-2 cursor-not-allowed truncate text-gray-400 select-none",
listGroupLabel: "pr-2 py-2 cursor-default select-none truncate font-bold text-gray-700",
listItem: "block transition duration-200 px-2 py-2 cursor-pointer select-none truncate rounded",
list: "max-h-72 overflow-y-auto",
menuButton:
"flex text-sm text-gray-500 border border-gray-300 rounded shadow-sm transition-all duration-300 focus:outline-none",
tagItem: "bg-gray-200 border rounded-sm flex space-x-1",
tagItemText: "text-gray-600 truncate cursor-default select-none",
tagItemIconContainer:
"flex items-center px-1 cursor-pointer rounded-r-sm hover:bg-red-200 hover:text-red-600",
tagItemIcon: "w-3 h-3 mt-0.5",
closeIcon: "w-5 h-5 p-0.5",
menu: "absolute z-10 w-full bg-white shadow-lg border rounded py-1 mt-1.5 text-sm text-gray-700"
};

export const THEME_DATA = {
bg: {
blue: "bg-blue-500",
Expand Down