Components: add more types, components and fixes (#591)
* changes * fixes * more fixes * even more changes
This commit is contained in:
parent
74c095975e
commit
f3d63106c2
23 changed files with 634 additions and 221 deletions
|
|
@ -25,6 +25,7 @@ import ClickableTab from "./tabs/ClickableTab";
|
|||
import ColorPickerTab from "./tabs/ColorPickerTab";
|
||||
import ComboboxTab from "./tabs/ComboboxTab";
|
||||
import DividerTab from "./tabs/DividerTab";
|
||||
import FocusLockTab from "./tabs/FocusLockTab";
|
||||
import GuildIconTab from "./tabs/GuildIconTab";
|
||||
import HeadingTab from "./tabs/HeadingTab";
|
||||
import LayerModalTab from "./tabs/LayerModalTab";
|
||||
|
|
@ -32,6 +33,7 @@ import ListboxTab from "./tabs/ListboxTab";
|
|||
import ModalTab from "./tabs/ModalTab";
|
||||
import NoticeTab from "./tabs/NoticeTab";
|
||||
import PaginatorTab from "./tabs/PaginatorTab";
|
||||
import PopoutTab from "./tabs/PopoutTab";
|
||||
import PopoverTab from "./tabs/PopoverTab";
|
||||
import ProgressBarTab from "./tabs/ProgressBarTab";
|
||||
import RadioGroupTab from "./tabs/RadioGroupTab";
|
||||
|
|
@ -51,6 +53,7 @@ import TimestampTab from "./tabs/TimestampTab";
|
|||
import ToastTab from "./tabs/ToastTab";
|
||||
import TooltipTab from "./tabs/TooltipTab";
|
||||
import TypographyTab from "./tabs/TypographyTab";
|
||||
import UserSummaryItemTab from "./tabs/UserSummaryItemTab";
|
||||
|
||||
|
||||
const TABS = [
|
||||
|
|
@ -68,6 +71,7 @@ const TABS = [
|
|||
{ id: "colorpicker", label: "ColorPicker" },
|
||||
{ id: "combobox", label: "Combobox" },
|
||||
{ id: "divider", label: "Divider" },
|
||||
{ id: "focuslock", label: "FocusLock" },
|
||||
{ id: "guildicon", label: "GuildIcon" },
|
||||
{ id: "heading", label: "Heading" },
|
||||
{ id: "layermodal", label: "LayerModal" },
|
||||
|
|
@ -75,6 +79,7 @@ const TABS = [
|
|||
{ id: "modal", label: "Modal" },
|
||||
{ id: "notice", label: "Notice" },
|
||||
{ id: "paginator", label: "Paginator" },
|
||||
{ id: "popout", label: "Popout" },
|
||||
{ id: "popover", label: "Popover" },
|
||||
{ id: "progressbar", label: "ProgressBar" },
|
||||
{ id: "radiogroup", label: "RadioGroup" },
|
||||
|
|
@ -94,6 +99,7 @@ const TABS = [
|
|||
{ id: "toast", label: "Toast" },
|
||||
{ id: "tooltip", label: "Tooltip" },
|
||||
{ id: "typography", label: "Typography" },
|
||||
{ id: "usersummaryitem", label: "UserSummaryItem" },
|
||||
|
||||
].sort((a, b) => a.label.localeCompare(b.label));
|
||||
|
||||
|
|
@ -114,6 +120,7 @@ const TAB_COMPONENTS: Record<TabId, React.ComponentType> = {
|
|||
colorpicker: ColorPickerTab,
|
||||
combobox: ComboboxTab,
|
||||
divider: DividerTab,
|
||||
focuslock: FocusLockTab,
|
||||
guildicon: GuildIconTab,
|
||||
heading: HeadingTab,
|
||||
layermodal: LayerModalTab,
|
||||
|
|
@ -121,6 +128,7 @@ const TAB_COMPONENTS: Record<TabId, React.ComponentType> = {
|
|||
modal: ModalTab,
|
||||
notice: NoticeTab,
|
||||
paginator: PaginatorTab,
|
||||
popout: PopoutTab,
|
||||
popover: PopoverTab,
|
||||
progressbar: ProgressBarTab,
|
||||
radiogroup: RadioGroupTab,
|
||||
|
|
@ -140,6 +148,7 @@ const TAB_COMPONENTS: Record<TabId, React.ComponentType> = {
|
|||
toast: ToastTab,
|
||||
tooltip: TooltipTab,
|
||||
typography: TypographyTab,
|
||||
usersummaryitem: UserSummaryItemTab,
|
||||
};
|
||||
|
||||
function ComponentsTab() {
|
||||
|
|
|
|||
|
|
@ -9,43 +9,31 @@ import { Card } from "@components/Card";
|
|||
import { Heading } from "@components/Heading";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy, findLazy } from "@webpack";
|
||||
import { filters, findByCodeLazy, findByPropsLazy, findComponentByCodeLazy, findLazy, mapMangledModuleLazy } from "@webpack";
|
||||
import {
|
||||
GuildStore,
|
||||
ListScrollerAuto,
|
||||
ListScrollerNone,
|
||||
ListScrollerThin,
|
||||
OAuth2AuthorizeModal,
|
||||
ScrollerAuto,
|
||||
ScrollerNone,
|
||||
ScrollerThin,
|
||||
SearchableSelect,
|
||||
TabBar,
|
||||
TooltipContainer,
|
||||
useEffect,
|
||||
useRef,
|
||||
UserStore,
|
||||
useState,
|
||||
} from "@webpack/common";
|
||||
|
||||
export {
|
||||
Button,
|
||||
Card,
|
||||
GuildStore,
|
||||
Heading,
|
||||
Paragraph,
|
||||
ScrollerAuto,
|
||||
ScrollerNone,
|
||||
ScrollerThin,
|
||||
TabBar,
|
||||
useEffect,
|
||||
useRef,
|
||||
UserStore,
|
||||
useState,
|
||||
};
|
||||
|
||||
export * from "../constants";
|
||||
|
||||
import {
|
||||
import { ToastPosition, ToastType } from "../constants";
|
||||
import type {
|
||||
AccordionProps,
|
||||
AnchorProps,
|
||||
AvatarProps,
|
||||
BadgeShapesType,
|
||||
CheckboxGroupOption,
|
||||
ChipProps,
|
||||
CircleBadgeProps,
|
||||
ClickableProps,
|
||||
|
|
@ -58,11 +46,9 @@ import {
|
|||
DiscordHeadingProps,
|
||||
DiscordTextProps,
|
||||
ExpressiveModalProps,
|
||||
GradientColor,
|
||||
FocusLockProps,
|
||||
GuildIconProps,
|
||||
HeadingVariant,
|
||||
IconBadgeProps,
|
||||
ListboxItem,
|
||||
ManaBaseRadioGroupProps,
|
||||
ManaButtonProps,
|
||||
ManaCalendarProps,
|
||||
|
|
@ -73,137 +59,61 @@ import {
|
|||
ManaListboxProps,
|
||||
ManaPopoverProps,
|
||||
ManaRichTooltipProps,
|
||||
ManaSelectOption,
|
||||
ManaSelectProps,
|
||||
ManaSwitchProps,
|
||||
ManaTextAreaProps,
|
||||
ManaTextButtonProps,
|
||||
ManaTextInputProps,
|
||||
ManaTooltipProps,
|
||||
ModalAction,
|
||||
ModalNotice,
|
||||
ModalProps,
|
||||
ModalRenderFn,
|
||||
NoticeProps,
|
||||
NoticeType,
|
||||
NumberBadgeProps,
|
||||
OpenModalOptions,
|
||||
PaginatorProps,
|
||||
PopoverAction,
|
||||
PopoutComponent,
|
||||
ProgressBarProps,
|
||||
RadioOption,
|
||||
SearchBarProps,
|
||||
SkeletonProps,
|
||||
SliderProps,
|
||||
SpinnerComponent,
|
||||
SpinnerProps,
|
||||
StandaloneRadioIndicatorProps,
|
||||
TabBarComponent,
|
||||
TabBarHeaderProps,
|
||||
TabBarItemProps,
|
||||
TabBarProps,
|
||||
TabBarSeparatorProps,
|
||||
TextBadgeProps,
|
||||
TextColor,
|
||||
TextVariant,
|
||||
TimestampProps,
|
||||
ToastData,
|
||||
ToastOptions,
|
||||
ToastPositionValue,
|
||||
ToastsModule,
|
||||
ToastTypeValue,
|
||||
UserSummaryItemProps,
|
||||
} from "../types";
|
||||
|
||||
export type {
|
||||
AccordionProps,
|
||||
AnchorProps,
|
||||
AvatarProps,
|
||||
BadgeShapesType,
|
||||
CheckboxGroupOption,
|
||||
ChipProps,
|
||||
CircleBadgeProps,
|
||||
ColorPickerProps,
|
||||
ColorPickerWithSwatchesProps,
|
||||
ColorSwatchProps,
|
||||
ConfirmModalProps,
|
||||
CustomColorButtonProps,
|
||||
DefaultColorButtonProps,
|
||||
DiscordHeadingProps,
|
||||
DiscordTextProps,
|
||||
ExpressiveModalProps,
|
||||
GradientColor,
|
||||
GuildIconProps,
|
||||
HeadingVariant,
|
||||
IconBadgeProps,
|
||||
ListboxItem,
|
||||
ManaBaseRadioGroupProps,
|
||||
ManaButtonProps,
|
||||
ManaCalendarProps,
|
||||
ManaCheckboxGroupProps,
|
||||
ManaCheckboxProps,
|
||||
ManaComboboxProps,
|
||||
ManaDatePickerProps,
|
||||
ManaListboxProps,
|
||||
ManaPopoverProps,
|
||||
ManaRichTooltipProps,
|
||||
ManaSelectOption,
|
||||
ManaSelectProps,
|
||||
ManaSwitchProps,
|
||||
ManaTextAreaProps,
|
||||
ManaTextButtonProps,
|
||||
ManaTextInputProps,
|
||||
ManaTooltipProps,
|
||||
ModalAction,
|
||||
ModalNotice,
|
||||
ModalProps,
|
||||
ModalRenderFn,
|
||||
NoticeProps,
|
||||
NoticeType,
|
||||
NumberBadgeProps,
|
||||
OpenModalOptions,
|
||||
PaginatorProps,
|
||||
PopoverAction,
|
||||
ProgressBarProps,
|
||||
RadioOption,
|
||||
SearchBarProps,
|
||||
SkeletonProps,
|
||||
SliderProps,
|
||||
SpinnerProps,
|
||||
StandaloneRadioIndicatorProps,
|
||||
TabBarComponent,
|
||||
TabBarHeaderProps,
|
||||
TabBarItemProps,
|
||||
TabBarProps,
|
||||
TabBarSeparatorProps,
|
||||
TextBadgeProps,
|
||||
TextColor,
|
||||
TextVariant,
|
||||
TimestampProps,
|
||||
ToastData,
|
||||
ToastOptions,
|
||||
ToastPositionValue,
|
||||
ToastsModule,
|
||||
ToastTypeValue,
|
||||
export * from "../constants";
|
||||
export type * from "../types";
|
||||
|
||||
export {
|
||||
Button,
|
||||
Card,
|
||||
GuildStore,
|
||||
Heading,
|
||||
ListScrollerAuto,
|
||||
ListScrollerNone,
|
||||
ListScrollerThin,
|
||||
OAuth2AuthorizeModal,
|
||||
Paragraph,
|
||||
ScrollerAuto,
|
||||
ScrollerNone,
|
||||
ScrollerThin,
|
||||
SearchableSelect,
|
||||
TabBar,
|
||||
TooltipContainer,
|
||||
useEffect,
|
||||
useRef,
|
||||
UserStore,
|
||||
useState,
|
||||
};
|
||||
|
||||
export const ToastType = {
|
||||
MESSAGE: "message",
|
||||
SUCCESS: "success",
|
||||
FAILURE: "failure",
|
||||
CUSTOM: "custom",
|
||||
CLIP: "clip",
|
||||
LINK: "link",
|
||||
FORWARD: "forward",
|
||||
BOOKMARK: "bookmark",
|
||||
CLOCK: "clock",
|
||||
} as const;
|
||||
|
||||
export const ToastPosition = {
|
||||
TOP: 0,
|
||||
BOTTOM: 1,
|
||||
} as const;
|
||||
|
||||
export const Anchor = findComponentByCodeLazy("anchorUnderlineOnHover", "useDefaultUnderlineStyles") as React.ComponentType<AnchorProps>;
|
||||
export const Clickable = findByCodeLazy("ignoreKeyPress", "renderNonInteractive") as React.ComponentType<ClickableProps>;
|
||||
|
||||
export const ManaButton = findComponentByCodeLazy('"data-mana-component":"button"') as React.ComponentType<ManaButtonProps>;
|
||||
export const ManaTextButton = findComponentByCodeLazy('"data-mana-component":"text-button"') as React.ComponentType<ManaTextButtonProps>;
|
||||
|
|
@ -212,25 +122,38 @@ export const ManaTextInput = findComponentByCodeLazy('"data-mana-component":"tex
|
|||
export const ManaTextArea = findComponentByCodeLazy('"data-mana-component":"text-area"') as React.ComponentType<ManaTextAreaProps>;
|
||||
export const ManaCheckbox = findComponentByCodeLazy('"data-mana-component":"checkbox"') as React.ComponentType<ManaCheckboxProps>;
|
||||
export const ManaSelect = findComponentByCodeLazy('"data-mana-component":"select"') as React.ComponentType<ManaSelectProps>;
|
||||
export const ManaTooltip = findComponentByCodeLazy("VoidTooltip cannot find DOM node") as React.ComponentType<ManaTooltipProps>;
|
||||
export const ManaCalendar = findComponentByCodeLazy('"data-mana-component":"calendar"') as React.ComponentType<ManaCalendarProps>;
|
||||
export const ManaDatePicker = findComponentByCodeLazy('"data-mana-component":"date-picker"') as React.ComponentType<ManaDatePickerProps>;
|
||||
export const ManaRichTooltip = findComponentByCodeLazy('"data-mana-component":"rich-tooltip"') as React.ComponentType<ManaRichTooltipProps>;
|
||||
export const ManaCombobox = findComponentByCodeLazy("itemToString", "multiSelect", "maxVisibleItems") as React.ComponentType<ManaComboboxProps>;
|
||||
export const ManaListbox = findComponentByCodeLazy('"data-mana-component":"listbox"') as React.ComponentType<ManaListboxProps>;
|
||||
export const ManaCheckboxGroup = findComponentByCodeLazy('"data-mana-component":"checkbox-group"') as React.ComponentType<ManaCheckboxGroupProps>;
|
||||
export const ManaBaseRadioGroup = findComponentByCodeLazy('"data-mana-component":"BaseRadioGroup"') as React.ComponentType<ManaBaseRadioGroupProps>;
|
||||
export const ManaCombobox = findComponentByCodeLazy("itemToString", "multiSelect", "maxVisibleItems") as React.ComponentType<ManaComboboxProps>;
|
||||
export const ManaTooltip = findComponentByCodeLazy("VoidTooltip cannot find DOM node") as React.ComponentType<ManaTooltipProps>;
|
||||
export const ManaRichTooltip = findComponentByCodeLazy('"data-mana-component":"rich-tooltip"') as React.ComponentType<ManaRichTooltipProps>;
|
||||
export const ManaPopover = findByCodeLazy("title", "body", "badge", "graphic", "actions", "textLink", "gradientColor", "popoverRef") as React.ComponentType<ManaPopoverProps>;
|
||||
export const StandaloneRadioIndicator = findComponentByCodeLazy("standaloneRadioIndicator", "animateIn", "animateOut") as React.ComponentType<StandaloneRadioIndicatorProps>;
|
||||
export const ManaPopover = findComponentByCodeLazy('"data-mana-component":"popover"') as React.ComponentType<ManaPopoverProps>;
|
||||
|
||||
export const Slider = findComponentByCodeLazy("stickToMarkers", "onMarkerRender", "grabberClassName") as React.ComponentType<SliderProps>;
|
||||
export const Avatar = findComponentByCodeLazy("statusTooltip", "statusBackdropColor", "isSpeaking") as React.ComponentType<AvatarProps>;
|
||||
export const ProgressBar = findComponentByCodeLazy("progressContainer", "labelledBy", "aria-valuenow") as React.ComponentType<ProgressBarProps>;
|
||||
export const Spinner = findComponentByCodeLazy("spinningCircleSimple", "pulsingEllipsis", "wanderingCubes") as unknown as SpinnerComponent;
|
||||
export const SearchBar = findComponentByCodeLazy("#{intl::SEARCH}", "clearable", "autoComplete") as React.ComponentType<SearchBarProps>;
|
||||
export const Paginator2 = findComponentByCodeLazy("#{intl::BACK}", "#{intl::NEXT}", "renderPageWrapper") as React.ComponentType<PaginatorProps>;
|
||||
export const Paginator = findComponentByCodeLazy("#{intl::BACK}", "#{intl::NEXT}", "renderPageWrapper") as React.ComponentType<PaginatorProps>;
|
||||
export const Notice = findComponentByCodeLazy("messageType", "iconDiv", "actionContainer") as React.ComponentType<NoticeProps>;
|
||||
export const Chip = findComponentByCodeLazy('variant:"eyebrow"', "chip,") as React.ComponentType<ChipProps>;
|
||||
export const Skeleton = findComponentByCodeLazy("productSkeletonCardContainer", "skipPulseAnimation") as React.ComponentType<SkeletonProps>;
|
||||
export const Accordion = findComponentByCodeLazy("accordionContainer", "onExpandedChange", "defaultExpanded") as React.ComponentType<AccordionProps>;
|
||||
export const Timestamp = findComponentByCodeLazy("#{intl::MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL}", "isVisibleOnlyOnHover") as React.ComponentType<TimestampProps>;
|
||||
export const GuildIcon = findComponentByCodeLazy("Masks.CLAN_ICON", "guildIconImage") as React.ComponentType<GuildIconProps>;
|
||||
export const ColorPicker = findComponentByCodeLazy("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", "showEyeDropper") as React.ComponentType<ColorPickerProps>;
|
||||
export const Popout: PopoutComponent = proxyLazy(() => findLazy(m => m?.y?.Animation && m.y.toString?.().includes("renderPopout")).y);
|
||||
export const FocusLock = findComponentByCodeLazy(".containerRef,{keyboardModeEnabled:") as React.ComponentType<FocusLockProps>;
|
||||
export const UserSummaryItem = findComponentByCodeLazy("popoutUserId") as React.ComponentType<UserSummaryItemProps>;
|
||||
|
||||
export const Animations = mapMangledModuleLazy(".assign({colorNames:", {
|
||||
Transition: filters.componentByCode('["items","children"]', ",null,"),
|
||||
animated: filters.byProps("div", "text")
|
||||
});
|
||||
|
||||
const ColorPickerModule = findLazy(m => {
|
||||
const values = Object.values(m);
|
||||
|
|
@ -273,23 +196,28 @@ function getBadgeExport(check: (fn: Function) => boolean): React.ComponentType<a
|
|||
return (() => null) as React.ComponentType<any>;
|
||||
}
|
||||
|
||||
export const NumberBadge: React.ComponentType<NumberBadgeProps> = proxyLazy(() => getBadgeExport(fn => fn.toString().includes("numberBadge")));
|
||||
export const TextBadge: React.ComponentType<TextBadgeProps> = proxyLazy(() => getBadgeExport(fn => fn.toString().includes("textBadge")));
|
||||
export const IconBadge: React.ComponentType<IconBadgeProps> = proxyLazy(() => getBadgeExport(fn => fn.toString().includes("iconBadge")));
|
||||
export const CircleBadge: React.ComponentType<CircleBadgeProps> = proxyLazy(() => getBadgeExport(fn => fn.toString().includes("disableColor") && !fn.toString().includes("iconBadge") && !fn.toString().includes("textBadge") && !fn.toString().includes("numberBadge") && !fn.toString().includes("premiumBadge")));
|
||||
export const BadgeShapes = proxyLazy(() => Object.values(BadgeModule).find(v => typeof v === "object" && (v as BadgeShapesType)?.ROUND) as BadgeShapesType ?? { ROUND: "", ROUND_LEFT: "", ROUND_RIGHT: "", SQUARE: "" });
|
||||
|
||||
export const Clickable = findByCodeLazy("ignoreKeyPress", "renderNonInteractive") as React.ComponentType<ClickableProps>;
|
||||
|
||||
export const Chip = findComponentByCodeLazy('variant:"eyebrow"', "chip,") as React.ComponentType<ChipProps>;
|
||||
|
||||
export const Skeleton = findComponentByCodeLazy("productSkeletonCardContainer", "skipPulseAnimation") as React.ComponentType<SkeletonProps>;
|
||||
|
||||
export const Accordion = findComponentByCodeLazy("accordionContainer", "onExpandedChange", "defaultExpanded") as React.ComponentType<AccordionProps>;
|
||||
|
||||
export const Timestamp = findComponentByCodeLazy("isVisibleOnlyOnHover", "cozyAlt", "timestampFormat") as React.ComponentType<TimestampProps>;
|
||||
|
||||
export const GuildIcon = findComponentByCodeLazy("getGuildIconURL", "acronym") as React.ComponentType<GuildIconProps>;
|
||||
export const NumberBadge: React.ComponentType<NumberBadgeProps> = proxyLazy(() =>
|
||||
getBadgeExport(fn => fn.toString().includes("numberBadge"))
|
||||
);
|
||||
export const TextBadge: React.ComponentType<TextBadgeProps> = proxyLazy(() =>
|
||||
getBadgeExport(fn => fn.toString().includes("textBadge"))
|
||||
);
|
||||
export const IconBadge: React.ComponentType<IconBadgeProps> = proxyLazy(() =>
|
||||
getBadgeExport(fn => fn.toString().includes("iconBadge"))
|
||||
);
|
||||
export const CircleBadge: React.ComponentType<CircleBadgeProps> = proxyLazy(() =>
|
||||
getBadgeExport(fn =>
|
||||
fn.toString().includes("disableColor") &&
|
||||
!fn.toString().includes("iconBadge") &&
|
||||
!fn.toString().includes("textBadge") &&
|
||||
!fn.toString().includes("numberBadge") &&
|
||||
!fn.toString().includes("premiumBadge")
|
||||
)
|
||||
);
|
||||
export const BadgeShapes = proxyLazy(() =>
|
||||
Object.values(BadgeModule).find(v => typeof v === "object" && (v as BadgeShapesType)?.ROUND) as BadgeShapesType ??
|
||||
{ ROUND: "", ROUND_LEFT: "", ROUND_RIGHT: "", SQUARE: "" }
|
||||
);
|
||||
|
||||
const ToastsModule = findByPropsLazy("showToast", "popToast");
|
||||
export const Toasts: ToastsModule = proxyLazy(() => ({
|
||||
|
|
@ -300,7 +228,9 @@ export const Toasts: ToastsModule = proxyLazy(() => ({
|
|||
pop: () => ToastsModule.popToast(),
|
||||
create: (message: string, type: ToastTypeValue, options?: ToastOptions) => ToastsModule.createToast(message, type, options),
|
||||
}));
|
||||
export const showToast = (message: string, type: ToastTypeValue = ToastType.MESSAGE) => ToastsModule.showToast(ToastsModule.createToast(message, type));
|
||||
|
||||
export const showToast = (message: string, type: ToastTypeValue = ToastType.MESSAGE) =>
|
||||
ToastsModule.showToast(ToastsModule.createToast(message, type));
|
||||
|
||||
const ModalModule = findByPropsLazy("Modal", "ConfirmModal");
|
||||
const ModalAPIModule = findByPropsLazy("openModal", "closeModal");
|
||||
|
|
@ -309,20 +239,25 @@ export const Modal: React.ComponentType<ModalProps> = proxyLazy(() => ModalModul
|
|||
export const ConfirmModal: React.ComponentType<ConfirmModalProps> = proxyLazy(() => ModalModule.ConfirmModal);
|
||||
export const ExpressiveModal: React.ComponentType<ExpressiveModalProps> = proxyLazy(() => ModalModule.ExpressiveModal);
|
||||
|
||||
export const openModal = (render: ModalRenderFn, options?: OpenModalOptions): string => ModalAPIModule.openModal(render, options);
|
||||
export const closeModal = (modalKey: string): void => ModalAPIModule.closeModal(modalKey);
|
||||
export const closeAllModals = (): void => ModalAPIModule.closeAllModals();
|
||||
export const hasModalOpen = (modalKey: string): boolean => ModalAPIModule.hasModalOpen(modalKey);
|
||||
export const hasAnyModalOpen = (): boolean => ModalAPIModule.hasAnyModalOpen();
|
||||
export const openModal = (render: ModalRenderFn, options?: OpenModalOptions): string =>
|
||||
ModalAPIModule.openModal(render, options);
|
||||
export const closeModal = (modalKey: string): void =>
|
||||
ModalAPIModule.closeModal(modalKey);
|
||||
export const closeAllModals = (): void =>
|
||||
ModalAPIModule.closeAllModals();
|
||||
export const hasModalOpen = (modalKey: string): boolean =>
|
||||
ModalAPIModule.hasModalOpen(modalKey);
|
||||
export const hasAnyModalOpen = (): boolean =>
|
||||
ModalAPIModule.hasAnyModalOpen();
|
||||
|
||||
const DiscordTextModule = findByPropsLazy("Heading", "Text");
|
||||
|
||||
export function DiscordHeading(props: DiscordHeadingProps) {
|
||||
const Component = DiscordTextModule.Heading;
|
||||
return <Component {...props} />;
|
||||
return <DiscordTextModule.Heading {...props} />;
|
||||
}
|
||||
|
||||
export function DiscordText(props: DiscordTextProps) {
|
||||
const Component = DiscordTextModule.Text;
|
||||
return <Component {...props} />;
|
||||
return <DiscordTextModule.Text {...props} />;
|
||||
}
|
||||
|
||||
export function Divider({ className, style, ...restProps }: React.ComponentPropsWithoutRef<"hr">) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { BadgeShapes, CircleBadge, IconBadge, NumberBadge, Paragraph,TextBadge } from "..";
|
||||
import { BadgeShapes, CircleBadge, IconBadge, NumberBadge, Paragraph, TextBadge } from "..";
|
||||
import { EquicordIcon } from "../icons/EquicordIcon";
|
||||
import { SectionWrapper } from "../SectionWrapper";
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ export default function BadgeTab() {
|
|||
|
||||
<SectionWrapper title="Props">
|
||||
<Paragraph color="text-muted">
|
||||
NumberBadge: count, maxDigits?, color?, disableColor?, shape?, className?, style?
|
||||
NumberBadge: count, color?, disableColor?, shape?, className?, style?, renderBadgeCount?
|
||||
</Paragraph>
|
||||
<Paragraph color="text-muted">
|
||||
TextBadge: text, color?, disableColor?, shape?, className?, style?
|
||||
|
|
|
|||
|
|
@ -4,31 +4,35 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Chip , Paragraph } from "..";
|
||||
import { Chip, Paragraph } from "..";
|
||||
import { SectionWrapper } from "../SectionWrapper";
|
||||
|
||||
const CHIP_COLORS = ["blurple", "purple", "green", "orange", "yellow", "pink", "red", "gray"] as const;
|
||||
const CHIP_SHADES = ["Light", "Medium", "Dark"] as const;
|
||||
|
||||
export default function ChipTab() {
|
||||
return (
|
||||
<div className="vc-compfinder-section">
|
||||
<SectionWrapper title="Colors">
|
||||
<div className="vc-compfinder-grid">
|
||||
<Chip text="Blurple" variant="blurpleMedium" />
|
||||
<Chip text="Purple" variant="purpleMedium" />
|
||||
<Chip text="Green" variant="greenMedium" />
|
||||
<Chip text="Orange" variant="orangeMedium" />
|
||||
<Chip text="Yellow" variant="yellowMedium" />
|
||||
<Chip text="Pink" variant="pinkMedium" />
|
||||
<Chip text="Red" variant="redMedium" />
|
||||
<Chip text="Gray" variant="grayMedium" />
|
||||
</div>
|
||||
</SectionWrapper>
|
||||
|
||||
<SectionWrapper title="Shades">
|
||||
<div className="vc-compfinder-grid">
|
||||
<Chip text="Light" variant="blurpleLight" />
|
||||
<Chip text="Medium" variant="blurpleMedium" />
|
||||
<Chip text="Dark" variant="blurpleDark" />
|
||||
</div>
|
||||
<SectionWrapper title="All Variants">
|
||||
<Paragraph color="text-muted" style={{ marginBottom: 8 }}>
|
||||
All 24 chip variants: 8 colors × 3 shades.
|
||||
</Paragraph>
|
||||
{CHIP_SHADES.map(shade => (
|
||||
<div key={shade} style={{ marginBottom: 16 }}>
|
||||
<Paragraph color="text-muted" style={{ marginBottom: 8, fontWeight: 600 }}>
|
||||
{shade}
|
||||
</Paragraph>
|
||||
<div className="vc-compfinder-grid">
|
||||
{CHIP_COLORS.map(color => (
|
||||
<Chip
|
||||
key={`${color}${shade}`}
|
||||
text={color}
|
||||
variant={`${color}${shade}` as any}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</SectionWrapper>
|
||||
|
||||
<SectionWrapper title="Use Cases">
|
||||
|
|
@ -49,6 +53,15 @@ export default function ChipTab() {
|
|||
<Paragraph color="text-muted">
|
||||
• variant?: ChipVariant - Color variant (default: "grayLight")
|
||||
</Paragraph>
|
||||
<Paragraph color="text-muted" style={{ marginTop: 8 }}>
|
||||
<strong>Variant Format:</strong> {"{color}{shade}"} where:
|
||||
</Paragraph>
|
||||
<Paragraph color="text-muted">
|
||||
• Colors: blurple, purple, green, orange, yellow, pink, red, gray
|
||||
</Paragraph>
|
||||
<Paragraph color="text-muted">
|
||||
• Shades: Light, Medium, Dark
|
||||
</Paragraph>
|
||||
</SectionWrapper>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { FocusLock, ManaButton, ManaTextInput, Paragraph, useRef, useState } from "..";
|
||||
import { SectionWrapper } from "../SectionWrapper";
|
||||
|
||||
export default function FocusLockTab() {
|
||||
const [focusLockEnabled, setFocusLockEnabled] = useState(false);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
|
||||
return (
|
||||
<div className="vc-compfinder-section">
|
||||
<SectionWrapper title="FocusLock Demo">
|
||||
<Paragraph color="text-muted" style={{ marginBottom: 8 }}>
|
||||
FocusLock traps keyboard focus within a container. Enable it and try tabbing - focus stays inside the box.
|
||||
</Paragraph>
|
||||
<ManaButton
|
||||
variant={focusLockEnabled ? "primary" : "secondary"}
|
||||
text={focusLockEnabled ? "Disable Focus Lock" : "Enable Focus Lock"}
|
||||
onClick={() => setFocusLockEnabled(!focusLockEnabled)}
|
||||
style={{ marginBottom: 16 }}
|
||||
/>
|
||||
<div
|
||||
ref={containerRef}
|
||||
style={{
|
||||
padding: 16,
|
||||
border: `2px solid ${focusLockEnabled ? "var(--brand-500)" : "var(--background-modifier-accent)"}`,
|
||||
borderRadius: 8,
|
||||
background: "var(--background-secondary)",
|
||||
}}
|
||||
>
|
||||
{focusLockEnabled && (
|
||||
<FocusLock containerRef={containerRef}>
|
||||
<Paragraph style={{ marginBottom: 12 }}>
|
||||
Focus is now trapped! Tab between these elements:
|
||||
</Paragraph>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
||||
<ManaTextInput
|
||||
value={inputValue}
|
||||
onChange={setInputValue}
|
||||
placeholder="First input"
|
||||
/>
|
||||
<ManaTextInput
|
||||
value=""
|
||||
onChange={() => { }}
|
||||
placeholder="Second input"
|
||||
/>
|
||||
<div style={{ display: "flex", gap: 8 }}>
|
||||
<ManaButton variant="secondary" text="Button 1" onClick={() => { }} />
|
||||
<ManaButton variant="secondary" text="Button 2" onClick={() => { }} />
|
||||
<ManaButton
|
||||
variant="primary"
|
||||
text="Exit Focus Lock"
|
||||
onClick={() => setFocusLockEnabled(false)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</FocusLock>
|
||||
)}
|
||||
{!focusLockEnabled && (
|
||||
<Paragraph color="text-muted">
|
||||
Click "Enable Focus Lock" to activate the focus trap demo.
|
||||
</Paragraph>
|
||||
)}
|
||||
</div>
|
||||
</SectionWrapper>
|
||||
|
||||
<SectionWrapper title="Use Cases">
|
||||
<Paragraph color="text-muted">• Modal dialogs - Keep focus within the modal</Paragraph>
|
||||
<Paragraph color="text-muted">• Dropdown menus - Trap focus while open</Paragraph>
|
||||
<Paragraph color="text-muted">• Settings panels - Improve keyboard navigation</Paragraph>
|
||||
<Paragraph color="text-muted">• Accessibility - Ensure screen readers stay in context</Paragraph>
|
||||
</SectionWrapper>
|
||||
|
||||
<SectionWrapper title="Props">
|
||||
<Paragraph color="text-muted">• containerRef - React ref to the container element</Paragraph>
|
||||
<Paragraph color="text-muted">• keyboardModeEnabled - Enable keyboard focus trapping</Paragraph>
|
||||
<Paragraph color="text-muted">• children - Content to render inside the focus trap</Paragraph>
|
||||
</SectionWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Paginator2 as Paginator, Paragraph, useState } from "..";
|
||||
import { Paginator, Paragraph, useState } from "..";
|
||||
import { SectionWrapper } from "../SectionWrapper";
|
||||
|
||||
export default function PaginatorTab() {
|
||||
|
|
|
|||
124
src/equicordplugins/components.dev/components/tabs/PopoutTab.tsx
Normal file
124
src/equicordplugins/components.dev/components/tabs/PopoutTab.tsx
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Clickable, ManaButton, Paragraph, Popout, useState } from "..";
|
||||
import { SectionWrapper } from "../SectionWrapper";
|
||||
|
||||
export default function PopoutTab() {
|
||||
const [showBasic, setShowBasic] = useState(false);
|
||||
const [showPositions, setShowPositions] = useState<Record<string, boolean>>({});
|
||||
|
||||
const positions = ["top", "bottom", "left", "right"] as const;
|
||||
|
||||
return (
|
||||
<div className="vc-compfinder-section">
|
||||
<SectionWrapper title="Basic Popout">
|
||||
<Paragraph color="text-muted" style={{ marginBottom: 8 }}>
|
||||
Click the button to toggle a popout.
|
||||
</Paragraph>
|
||||
<Popout
|
||||
shouldShow={showBasic}
|
||||
onRequestClose={() => setShowBasic(false)}
|
||||
renderPopout={() => (
|
||||
<div style={{ padding: 16, background: "var(--background-floating)", borderRadius: 8 }}>
|
||||
<Paragraph>This is a popout!</Paragraph>
|
||||
<ManaButton
|
||||
variant="secondary"
|
||||
text="Close"
|
||||
onClick={() => setShowBasic(false)}
|
||||
style={{ marginTop: 8 }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{(props, { isShown }) => (
|
||||
<Clickable {...props}>
|
||||
<ManaButton
|
||||
variant={isShown ? "primary" : "secondary"}
|
||||
text={isShown ? "Popout Open" : "Open Popout"}
|
||||
onClick={() => setShowBasic(!showBasic)}
|
||||
/>
|
||||
</Clickable>
|
||||
)}
|
||||
</Popout>
|
||||
</SectionWrapper>
|
||||
|
||||
<SectionWrapper title="Positions">
|
||||
<Paragraph color="text-muted" style={{ marginBottom: 8 }}>
|
||||
Popout can appear in different positions.
|
||||
</Paragraph>
|
||||
<div className="vc-compfinder-grid">
|
||||
{positions.map(position => (
|
||||
<Popout
|
||||
key={position}
|
||||
position={position}
|
||||
shouldShow={showPositions[position] ?? false}
|
||||
onRequestClose={() => setShowPositions(p => ({ ...p, [position]: false }))}
|
||||
renderPopout={() => (
|
||||
<div style={{ padding: 12, background: "var(--background-floating)", borderRadius: 8 }}>
|
||||
<Paragraph>Position: {position}</Paragraph>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{(props, { isShown }) => (
|
||||
<Clickable {...props}>
|
||||
<ManaButton
|
||||
variant={isShown ? "primary" : "secondary"}
|
||||
text={position}
|
||||
onClick={() => setShowPositions(p => ({ ...p, [position]: !p[position] }))}
|
||||
/>
|
||||
</Clickable>
|
||||
)}
|
||||
</Popout>
|
||||
))}
|
||||
</div>
|
||||
</SectionWrapper>
|
||||
|
||||
<SectionWrapper title="Animation Types">
|
||||
<Paragraph color="text-muted" style={{ marginBottom: 8 }}>
|
||||
Popout supports different animation types via Popout.Animation.
|
||||
</Paragraph>
|
||||
<div className="vc-compfinder-grid">
|
||||
{(["NONE", "TRANSLATE", "SCALE", "FADE"] as const).map(anim => (
|
||||
<Popout
|
||||
key={anim}
|
||||
animation={Popout.Animation[anim]}
|
||||
shouldShow={showPositions[`anim-${anim}`] ?? false}
|
||||
onRequestClose={() => setShowPositions(p => ({ ...p, [`anim-${anim}`]: false }))}
|
||||
renderPopout={() => (
|
||||
<div style={{ padding: 12, background: "var(--background-floating)", borderRadius: 8 }}>
|
||||
<Paragraph>Animation: {anim}</Paragraph>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{(props, { isShown }) => (
|
||||
<Clickable {...props}>
|
||||
<ManaButton
|
||||
variant={isShown ? "primary" : "secondary"}
|
||||
text={anim}
|
||||
onClick={() => setShowPositions(p => ({ ...p, [`anim-${anim}`]: !p[`anim-${anim}`] }))}
|
||||
/>
|
||||
</Clickable>
|
||||
)}
|
||||
</Popout>
|
||||
))}
|
||||
</div>
|
||||
</SectionWrapper>
|
||||
|
||||
<SectionWrapper title="Props">
|
||||
<Paragraph color="text-muted">• children - Render function (props, data) => ReactNode</Paragraph>
|
||||
<Paragraph color="text-muted">• renderPopout - Render function for popout content</Paragraph>
|
||||
<Paragraph color="text-muted">• shouldShow - Control visibility</Paragraph>
|
||||
<Paragraph color="text-muted">• position - top, bottom, left, right, center, window_center</Paragraph>
|
||||
<Paragraph color="text-muted">• align - left, right, center, top, bottom</Paragraph>
|
||||
<Paragraph color="text-muted">• animation - Popout.Animation.NONE/TRANSLATE/SCALE/FADE</Paragraph>
|
||||
<Paragraph color="text-muted">• spacing - Distance from target (default 8)</Paragraph>
|
||||
<Paragraph color="text-muted">• onRequestOpen/onRequestClose - Callbacks</Paragraph>
|
||||
<Paragraph color="text-muted">• autoInvert - Auto-flip if near edge (default true)</Paragraph>
|
||||
</SectionWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -12,48 +12,23 @@ export default function SkeletonTab() {
|
|||
|
||||
return (
|
||||
<div className="vc-compfinder-section">
|
||||
<SectionWrapper title="Default Skeleton">
|
||||
<SectionWrapper title="Skeleton">
|
||||
<Paragraph color="text-muted" style={{ marginBottom: 8 }}>
|
||||
Loading placeholder with pulse animation. Requires "full-motion" parent class for animation.
|
||||
</Paragraph>
|
||||
<div className="vc-compfinder-grid full-motion">
|
||||
<Skeleton />
|
||||
<Skeleton />
|
||||
<Skeleton />
|
||||
</div>
|
||||
</SectionWrapper>
|
||||
|
||||
<SectionWrapper title="Animation Toggle">
|
||||
<Paragraph color="text-muted" style={{ marginBottom: 8 }}>
|
||||
Use skipPulseAnimation to disable the pulse effect.
|
||||
Loading placeholder with pulse animation. Used in Discord's shop for product cards.
|
||||
</Paragraph>
|
||||
<ManaButton
|
||||
variant="secondary"
|
||||
text={animated ? "Disable Animation" : "Enable Animation"}
|
||||
onClick={() => setAnimated(!animated)}
|
||||
style={{ marginBottom: 16 }}
|
||||
/>
|
||||
<div className="vc-compfinder-grid full-motion" style={{ marginTop: 16 }}>
|
||||
<Skeleton skipPulseAnimation={!animated} />
|
||||
<div className="full-motion">
|
||||
<Skeleton skipPulseAnimation={!animated} />
|
||||
</div>
|
||||
</SectionWrapper>
|
||||
|
||||
<SectionWrapper title="Props">
|
||||
<Paragraph color="text-muted">
|
||||
• skipPulseAnimation?: boolean - Disable pulse animation (default: false)
|
||||
</Paragraph>
|
||||
</SectionWrapper>
|
||||
|
||||
<SectionWrapper title="Notes">
|
||||
<Paragraph color="text-muted">
|
||||
• This is a product card skeleton used in Discord's shop.
|
||||
</Paragraph>
|
||||
<Paragraph color="text-muted">
|
||||
• Animation requires a parent with "full-motion" class (Discord's reduced motion setting).
|
||||
</Paragraph>
|
||||
<Paragraph color="text-muted">
|
||||
• The skeleton has fixed dimensions designed for product cards.
|
||||
</Paragraph>
|
||||
<Paragraph color="text-muted">• skipPulseAnimation?: boolean - Disable pulse animation</Paragraph>
|
||||
</SectionWrapper>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -14,8 +14,10 @@ const TOAST_TYPES = [
|
|||
{ type: ToastType.CLIP, label: "CLIP", description: "Clip/copy toast" },
|
||||
{ type: ToastType.LINK, label: "LINK", description: "Link copied toast" },
|
||||
{ type: ToastType.FORWARD, label: "FORWARD", description: "Forward action toast" },
|
||||
{ type: ToastType.INVITE, label: "INVITE", description: "Invite action toast" },
|
||||
{ type: ToastType.BOOKMARK, label: "BOOKMARK", description: "Bookmark action toast" },
|
||||
{ type: ToastType.CLOCK, label: "CLOCK", description: "Clock/timer toast" },
|
||||
{ type: ToastType.AI, label: "AI", description: "AI action toast" },
|
||||
] as const;
|
||||
|
||||
export default function ToastTab() {
|
||||
|
|
@ -108,8 +110,10 @@ export default function ToastTab() {
|
|||
<Paragraph color="text-muted">• CLIP - Clip/scissors icon</Paragraph>
|
||||
<Paragraph color="text-muted">• LINK - Link icon</Paragraph>
|
||||
<Paragraph color="text-muted">• FORWARD - Forward arrow icon</Paragraph>
|
||||
<Paragraph color="text-muted">• INVITE - Invite icon</Paragraph>
|
||||
<Paragraph color="text-muted">• BOOKMARK - Bookmark icon</Paragraph>
|
||||
<Paragraph color="text-muted">• CLOCK - Clock icon</Paragraph>
|
||||
<Paragraph color="text-muted">• AI - AI icon</Paragraph>
|
||||
</SectionWrapper>
|
||||
|
||||
<SectionWrapper title="ToastPosition">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Paragraph, UserStore, UserSummaryItem } from "..";
|
||||
import { SectionWrapper } from "../SectionWrapper";
|
||||
|
||||
export default function UserSummaryItemTab() {
|
||||
const currentUser = UserStore.getCurrentUser();
|
||||
const users = currentUser ? [currentUser, currentUser, currentUser, currentUser, currentUser] : [];
|
||||
|
||||
return (
|
||||
<div className="vc-compfinder-section">
|
||||
<SectionWrapper title="Basic UserSummaryItem">
|
||||
<Paragraph color="text-muted" style={{ marginBottom: 8 }}>
|
||||
Displays a row of user avatars with overflow indicator.
|
||||
</Paragraph>
|
||||
<div className="vc-compfinder-grid">
|
||||
<UserSummaryItem users={users} max={3} />
|
||||
</div>
|
||||
</SectionWrapper>
|
||||
|
||||
<SectionWrapper title="Different Sizes">
|
||||
<Paragraph color="text-muted" style={{ marginBottom: 8 }}>
|
||||
Available sizes: 16, 24, 32, 56.
|
||||
</Paragraph>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 16 }}>
|
||||
<Paragraph color="text-muted" style={{ width: 50 }}>16:</Paragraph>
|
||||
<UserSummaryItem users={users} max={4} size={16} />
|
||||
</div>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 16 }}>
|
||||
<Paragraph color="text-muted" style={{ width: 50 }}>24:</Paragraph>
|
||||
<UserSummaryItem users={users} max={4} size={24} />
|
||||
</div>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 16 }}>
|
||||
<Paragraph color="text-muted" style={{ width: 50 }}>32:</Paragraph>
|
||||
<UserSummaryItem users={users} max={4} size={32} />
|
||||
</div>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 16 }}>
|
||||
<Paragraph color="text-muted" style={{ width: 50 }}>56:</Paragraph>
|
||||
<UserSummaryItem users={users} max={4} size={56} />
|
||||
</div>
|
||||
</div>
|
||||
</SectionWrapper>
|
||||
|
||||
<SectionWrapper title="Max Users">
|
||||
<Paragraph color="text-muted" style={{ marginBottom: 8 }}>
|
||||
Control how many avatars to show before overflow.
|
||||
</Paragraph>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 16 }}>
|
||||
<Paragraph color="text-muted" style={{ width: 80 }}>max=2:</Paragraph>
|
||||
<UserSummaryItem users={users} max={2} />
|
||||
</div>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 16 }}>
|
||||
<Paragraph color="text-muted" style={{ width: 80 }}>max=3:</Paragraph>
|
||||
<UserSummaryItem users={users} max={3} />
|
||||
</div>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 16 }}>
|
||||
<Paragraph color="text-muted" style={{ width: 80 }}>max=5:</Paragraph>
|
||||
<UserSummaryItem users={users} max={5} />
|
||||
</div>
|
||||
</div>
|
||||
</SectionWrapper>
|
||||
|
||||
<SectionWrapper title="With Popout">
|
||||
<Paragraph color="text-muted" style={{ marginBottom: 8 }}>
|
||||
Enable user popout on avatar click.
|
||||
</Paragraph>
|
||||
<div className="vc-compfinder-grid">
|
||||
<UserSummaryItem users={users} max={3} showUserPopout />
|
||||
</div>
|
||||
</SectionWrapper>
|
||||
|
||||
<SectionWrapper title="Props">
|
||||
<Paragraph color="text-muted">• users - Array of User objects (or null for empty slots)</Paragraph>
|
||||
<Paragraph color="text-muted">• max - Maximum avatars to show (default 10)</Paragraph>
|
||||
<Paragraph color="text-muted">• count - Override count for "+N" display</Paragraph>
|
||||
<Paragraph color="text-muted">• size - 16, 24, 32, or 56 (default 24)</Paragraph>
|
||||
<Paragraph color="text-muted">• guildId - Guild context for avatars</Paragraph>
|
||||
<Paragraph color="text-muted">• showUserPopout - Enable user popout on click</Paragraph>
|
||||
<Paragraph color="text-muted">• showDefaultAvatarsForNullUsers - Show default avatar for null</Paragraph>
|
||||
<Paragraph color="text-muted">• renderUser - Custom render function for each user</Paragraph>
|
||||
<Paragraph color="text-muted">• renderMoreUsers - Custom render for overflow indicator</Paragraph>
|
||||
<Paragraph color="text-muted">• hideMoreUsers - Hide the "+N" overflow count</Paragraph>
|
||||
</SectionWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -165,3 +165,22 @@ export type PopoverSize = typeof PopoverSizes[number];
|
|||
|
||||
export const SliderOrientations = ["horizontal", "vertical"] as const;
|
||||
export type SliderOrientation = typeof SliderOrientations[number];
|
||||
|
||||
export const ToastType = {
|
||||
MESSAGE: "message",
|
||||
SUCCESS: "success",
|
||||
FAILURE: "failure",
|
||||
CUSTOM: "custom",
|
||||
CLIP: "clip",
|
||||
LINK: "link",
|
||||
FORWARD: "forward",
|
||||
INVITE: "invite",
|
||||
BOOKMARK: "bookmark",
|
||||
CLOCK: "clock",
|
||||
AI: "ai",
|
||||
} as const;
|
||||
|
||||
export const ToastPosition = {
|
||||
TOP: 0,
|
||||
BOTTOM: 1,
|
||||
} as const;
|
||||
|
|
|
|||
|
|
@ -20,15 +20,24 @@ export interface AvatarProps {
|
|||
size?: AvatarSize;
|
||||
status?: AvatarStatus;
|
||||
statusColor?: string;
|
||||
statusBackdropColor?: string;
|
||||
isMobile?: boolean;
|
||||
isTyping?: boolean;
|
||||
isSpeaking?: boolean;
|
||||
isLatched?: boolean;
|
||||
voiceDb?: number;
|
||||
speakingStylesConfig?: Record<string, any>;
|
||||
statusTooltip?: boolean;
|
||||
statusTooltipDelay?: number;
|
||||
avatarDecoration?: string;
|
||||
typingIndicatorRef?: React.Ref<any>;
|
||||
avatarContentRef?: React.Ref<any>;
|
||||
CutoutIcon?: React.ComponentType<any>;
|
||||
avatarTooltipAsset?: React.ReactNode;
|
||||
avatarTooltipText?: string;
|
||||
avatarTooltipTitle?: string;
|
||||
"aria-hidden"?: boolean;
|
||||
"aria-label"?: string;
|
||||
imageClassName?: string;
|
||||
className?: string;
|
||||
onClick?: (e: React.MouseEvent) => void;
|
||||
ref?: React.Ref<SVGSVGElement>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ export interface BadgeShapesType {
|
|||
|
||||
export interface NumberBadgeProps {
|
||||
count: number;
|
||||
maxDigits?: number;
|
||||
color?: string;
|
||||
disableColor?: boolean;
|
||||
shape?: string;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
renderBadgeCount?: (count: number) => string;
|
||||
}
|
||||
|
||||
export interface TextBadgeProps {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ export interface ManaCheckboxProps {
|
|||
leadingIcon?: React.ComponentType<any>;
|
||||
labelType?: CheckboxLabelType;
|
||||
usageVariant?: CheckboxUsageVariant;
|
||||
value?: string | number;
|
||||
labeledBy?: string;
|
||||
}
|
||||
|
||||
export interface CheckboxGroupOption {
|
||||
|
|
|
|||
11
src/equicordplugins/components.dev/types/focuslock.d.ts
vendored
Normal file
11
src/equicordplugins/components.dev/types/focuslock.d.ts
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export interface FocusLockProps {
|
||||
containerRef: React.RefObject<HTMLElement | null>;
|
||||
keyboardModeEnabled?: boolean;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
|
@ -15,12 +15,14 @@ export * from "./checkbox";
|
|||
export * from "./chip";
|
||||
export * from "./clickable";
|
||||
export * from "./colorpicker";
|
||||
export * from "./focuslock";
|
||||
export * from "./guildicon.d";
|
||||
export * from "./heading";
|
||||
export * from "./input";
|
||||
export * from "./modal";
|
||||
export * from "./notice";
|
||||
export * from "./paginator";
|
||||
export * from "./popout";
|
||||
export * from "./progress";
|
||||
export * from "./radio";
|
||||
export * from "./scroller";
|
||||
|
|
@ -33,3 +35,4 @@ export * from "./tabbar";
|
|||
export * from "./timestamp";
|
||||
export * from "./toast";
|
||||
export * from "./tooltip";
|
||||
export * from "./usersummary";
|
||||
|
|
@ -63,11 +63,14 @@ export interface ManaTextAreaProps {
|
|||
disabled?: boolean;
|
||||
readOnly?: boolean;
|
||||
rows?: number;
|
||||
autoFocus?: boolean;
|
||||
autosize?: boolean;
|
||||
maxLength?: number;
|
||||
minLength?: number;
|
||||
showCharacterCount?: boolean;
|
||||
showRemainingCharacterCount?: boolean;
|
||||
error?: string | boolean;
|
||||
defaultDirty?: boolean;
|
||||
fullWidth?: boolean;
|
||||
focusProps?: Record<string, any>;
|
||||
inputRef?: React.Ref<HTMLTextAreaElement>;
|
||||
|
|
|
|||
72
src/equicordplugins/components.dev/types/popout.d.ts
vendored
Normal file
72
src/equicordplugins/components.dev/types/popout.d.ts
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export type PopoutAnimationType = "1" | "2" | "3" | "4";
|
||||
|
||||
export type PopoutPosition = "top" | "bottom" | "left" | "right" | "center" | "window_center";
|
||||
|
||||
export type PopoutAlign = "left" | "right" | "center" | "top" | "bottom";
|
||||
|
||||
export interface PopoutChildrenProps {
|
||||
"aria-controls": string;
|
||||
"aria-expanded": boolean;
|
||||
onClick: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => void;
|
||||
onMouseDown: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
}
|
||||
|
||||
export interface PopoutChildrenData {
|
||||
isShown: boolean;
|
||||
position: PopoutPosition;
|
||||
}
|
||||
|
||||
export interface PopoutRenderProps {
|
||||
closePopout: () => void;
|
||||
isPositioned: boolean;
|
||||
nudge: number;
|
||||
position: PopoutPosition;
|
||||
setPopoutRef: (ref: any) => void;
|
||||
updatePosition: () => void;
|
||||
}
|
||||
|
||||
export interface PopoutProps {
|
||||
children: (props: PopoutChildrenProps, data: PopoutChildrenData) => React.ReactNode;
|
||||
renderPopout: (props: PopoutRenderProps) => React.ReactNode;
|
||||
shouldShow?: boolean;
|
||||
targetElementRef?: React.RefObject<HTMLElement>;
|
||||
onRequestOpen?: () => void;
|
||||
onRequestClose?: () => void;
|
||||
align?: PopoutAlign;
|
||||
animation?: PopoutAnimationType;
|
||||
animationPosition?: PopoutPosition;
|
||||
autoInvert?: boolean;
|
||||
nudgeAlignIntoViewport?: boolean;
|
||||
position?: PopoutPosition;
|
||||
positionKey?: string;
|
||||
popoutKey?: string;
|
||||
spacing?: number;
|
||||
preload?: boolean;
|
||||
fixed?: boolean;
|
||||
useRawTargetDimensions?: boolean;
|
||||
onShiftClick?: () => void;
|
||||
disablePointerEvents?: boolean;
|
||||
ignoreModalClicks?: boolean;
|
||||
scrollBehavior?: string;
|
||||
useMouseEnter?: boolean;
|
||||
layerContext?: any;
|
||||
clickTrap?: boolean;
|
||||
}
|
||||
|
||||
export interface PopoutAnimationEnum {
|
||||
NONE: "1";
|
||||
TRANSLATE: "2";
|
||||
SCALE: "3";
|
||||
FADE: "4";
|
||||
}
|
||||
|
||||
export type PopoutComponent = React.ComponentType<PopoutProps> & {
|
||||
Animation: PopoutAnimationEnum;
|
||||
};
|
||||
|
|
@ -17,6 +17,7 @@ export interface ManaBaseRadioGroupProps {
|
|||
value?: string | number;
|
||||
onChange?: (value: string | number) => void;
|
||||
disabled?: boolean;
|
||||
label?: string;
|
||||
"aria-labelledby"?: string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,15 +25,22 @@ export interface ManaSelectProps {
|
|||
shouldFocusWrap?: boolean;
|
||||
maxOptionsVisible?: number;
|
||||
wrapTags?: boolean;
|
||||
formatOption?: (option: ManaSelectOption) => React.ReactNode;
|
||||
name?: string;
|
||||
form?: string;
|
||||
autoComplete?: string;
|
||||
label?: string;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
export interface ManaComboboxProps {
|
||||
export interface ManaComboboxProps<T = string> {
|
||||
placeholder?: string;
|
||||
value?: string | string[];
|
||||
onChange?: (value: string | string[]) => void;
|
||||
value?: T | T[];
|
||||
onChange?: (value: T | T[]) => void;
|
||||
multiSelect?: boolean;
|
||||
autoFocus?: boolean;
|
||||
maxVisibleItems?: number;
|
||||
itemToString?: (item: T) => string;
|
||||
emptyStateText?: string;
|
||||
emptyStateHeader?: string;
|
||||
onQueryChange?: (query: string) => void;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export type ToastTypeValue = "message" | "success" | "failure" | "custom" | "clip" | "link" | "forward" | "bookmark" | "clock";
|
||||
export type ToastTypeValue = "message" | "success" | "failure" | "custom" | "clip" | "link" | "forward" | "invite" | "bookmark" | "clock" | "ai";
|
||||
|
||||
export type ToastPositionValue = 0 | 1;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,29 @@ import type { PopoverSize, TooltipAlign, TooltipColor, TooltipPosition } from ".
|
|||
export type { PopoverSize, TooltipAlign, TooltipColor, TooltipPosition };
|
||||
|
||||
export interface ManaTooltipProps {
|
||||
text: string;
|
||||
text: string | (() => React.ReactNode);
|
||||
position?: TooltipPosition;
|
||||
align?: TooltipAlign;
|
||||
color?: TooltipColor;
|
||||
spacing?: number;
|
||||
hideOnClick?: boolean;
|
||||
delay?: number;
|
||||
children: (props: any) => React.ReactNode;
|
||||
forceOpen?: boolean;
|
||||
shouldShow?: boolean;
|
||||
allowOverflow?: boolean;
|
||||
overflowOnly?: boolean;
|
||||
clickableOnMobile?: boolean;
|
||||
disableTooltipPointerEvents?: boolean;
|
||||
targetElementRef?: React.RefObject<HTMLElement>;
|
||||
tooltipClassName?: string;
|
||||
tooltipStyle?: React.CSSProperties;
|
||||
tooltipContentClassName?: string;
|
||||
tooltipPointerClassName?: string;
|
||||
onTooltipShow?: () => void;
|
||||
onTooltipHide?: () => void;
|
||||
onAnimationRest?: () => void;
|
||||
"aria-label"?: string | false;
|
||||
children: (props: Record<string, any>) => React.ReactNode;
|
||||
}
|
||||
|
||||
export interface ManaRichTooltipProps {
|
||||
|
|
@ -29,8 +44,13 @@ export interface ManaRichTooltipProps {
|
|||
position?: TooltipPosition;
|
||||
align?: TooltipAlign;
|
||||
spacing?: number;
|
||||
caretConfig?: { align?: "start" | "center" | "end"; };
|
||||
layerContext?: any;
|
||||
targetElementRef?: React.RefObject<HTMLElement>;
|
||||
anchorRef?: React.RefObject<HTMLElement>;
|
||||
positionKey?: string;
|
||||
ariaHidden?: boolean;
|
||||
children: React.ReactNode | ((props: any) => React.ReactNode);
|
||||
children: React.ReactNode | ((props: Record<string, any>) => React.ReactNode);
|
||||
}
|
||||
|
||||
export interface PopoverAction {
|
||||
|
|
|
|||
28
src/equicordplugins/components.dev/types/usersummary.d.ts
vendored
Normal file
28
src/equicordplugins/components.dev/types/usersummary.d.ts
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { User } from "@vencord/discord-types";
|
||||
|
||||
export type UserSummarySize = 16 | 24 | 32 | 56;
|
||||
|
||||
export interface UserSummaryItemProps {
|
||||
users: (User | null)[];
|
||||
max?: number;
|
||||
count?: number;
|
||||
size?: UserSummarySize;
|
||||
guildId?: string;
|
||||
showUserPopout?: boolean;
|
||||
showDefaultAvatarsForNullUsers?: boolean;
|
||||
renderUser?: (user: User | null, isLast: boolean, index: number) => React.ReactNode;
|
||||
renderMoreUsers?: (text: string, count: number) => React.ReactNode;
|
||||
renderIcon?: boolean;
|
||||
renderLeadingIcon?: (className: string) => React.ReactNode;
|
||||
dimEmptyUsers?: boolean;
|
||||
hideMoreUsers?: boolean;
|
||||
useFallbackUserForPopout?: boolean;
|
||||
extraDetail?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue