diff --git a/NOTICE b/NOTICE index 8605c258e3..b2fcf4deb4 100644 --- a/NOTICE +++ b/NOTICE @@ -101,6 +101,11 @@ https://github.com/microsoft/vscode-codicons Copyright (c) Microsoft Corporation and other contributors See https://github.com/microsoft/vscode-codicons/blob/main/LICENSE for license details. +Mantine UI +https://github.com/mantinedev/mantine +Copyright (c) 2021 Vitaly Rtishchev +See https://github.com/mantinedev/mantine/blob/master/LICENSE for license details. + We also use code from a large number of npm packages. For details, see: - https://github.com/prometheus/prometheus/blob/main/web/ui/react-app/package.json - https://github.com/prometheus/prometheus/blob/main/web/ui/react-app/package-lock.json diff --git a/web/ui/mantine-ui/eslint.config.mjs b/web/ui/mantine-ui/eslint.config.mjs index c3cc58920e..413560417d 100644 --- a/web/ui/mantine-ui/eslint.config.mjs +++ b/web/ui/mantine-ui/eslint.config.mjs @@ -16,7 +16,7 @@ const compat = new FlatCompat({ }); export default [{ - ignores: ['**/dist', '**/.eslintrc.cjs'], + ignores: ['**/dist', '**/.eslintrc.cjs', 'src/components/Accordion/**'], }, ...fixupConfigRules(compat.extends( 'eslint:recommended', 'plugin:@typescript-eslint/recommended', diff --git a/web/ui/mantine-ui/src/README-PROMETHEUS.md b/web/ui/mantine-ui/src/README-PROMETHEUS.md new file mode 100644 index 0000000000..6fd2b4814f --- /dev/null +++ b/web/ui/mantine-ui/src/README-PROMETHEUS.md @@ -0,0 +1,10 @@ +This is a temporary fork of the Accordion component from Mantine UI v8.3.6 with modifications specific to Prometheus. +The component has been modified to unmount children of collapsed panels to reduce page rendering times and +resource usage. + +According to Mantine author Vitaly, a similar feature has now been added to Mantine itself, but will only be +available in version 9.0.0 and later, quote from https://discord.com/channels/854810300876062770/1006447791498870784/threads/1428787320546525336: + +> I've managed to implement it, but only in 9.0 since it requires some breaking changes. Will be available next year + +So this Accordion fork can be removed once Prometheus upgrades to Mantine v9 or later. diff --git a/web/ui/mantine-ui/src/components/Accordion/Accordion.context.ts b/web/ui/mantine-ui/src/components/Accordion/Accordion.context.ts new file mode 100644 index 0000000000..63844ab80b --- /dev/null +++ b/web/ui/mantine-ui/src/components/Accordion/Accordion.context.ts @@ -0,0 +1,56 @@ +/* + * Some parts of this file are derived from Mantine UI (https://github.com/mantinedev/mantine) + * which is distributed under the MIT license: + * + * MIT License + * + * Copyright (c) 2021 Vitaly Rtishchev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Modifications to this file are licensed under the Apache License, Version 2.0. + */ + +import { createSafeContext, GetStylesApi } from "@mantine/core"; +import type { AccordionFactory } from "./Accordion"; +import { + AccordionChevronPosition, + AccordionHeadingOrder, +} from "./Accordion.types"; + +interface AccordionContext { + loop: boolean | undefined; + transitionDuration: number | undefined; + disableChevronRotation: boolean | undefined; + chevronPosition: AccordionChevronPosition | undefined; + order: AccordionHeadingOrder | undefined; + chevron: React.ReactNode; + onChange: (value: string) => void; + isItemActive: (value: string) => boolean; + getControlId: (value: string) => string; + getRegionId: (value: string) => string; + getStyles: GetStylesApi; + variant: string | undefined; + unstyled: boolean | undefined; +} + +export const [AccordionProvider, useAccordionContext] = + createSafeContext( + "Accordion component was not found in the tree" + ); diff --git a/web/ui/mantine-ui/src/components/Accordion/Accordion.module.css b/web/ui/mantine-ui/src/components/Accordion/Accordion.module.css new file mode 100644 index 0000000000..9bf3c6a3e0 --- /dev/null +++ b/web/ui/mantine-ui/src/components/Accordion/Accordion.module.css @@ -0,0 +1,204 @@ +/* + * Some parts of this file are derived from Mantine UI (https://github.com/mantinedev/mantine) + * which is distributed under the MIT license: + * + * MIT License + * + * Copyright (c) 2021 Vitaly Rtishchev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Modifications to this file are licensed under the Apache License, Version 2.0. + */ + +.root { + --accordion-radius: var(--mantine-radius-default); +} + +.panel { + overflow-wrap: break-word; +} + +.content { + padding: var(--mantine-spacing-md); + padding-top: calc(var(--mantine-spacing-xs) / 2); +} + +.itemTitle { + margin: 0; + padding: 0; +} + +.control { + width: 100%; + display: flex; + align-items: center; + flex-direction: row-reverse; + padding-inline: var(--mantine-spacing-md); + opacity: 1; + cursor: pointer; + background-color: transparent; + color: var(--mantine-color-bright); + + &:where([data-chevron-position="left"]) { + flex-direction: row; + padding-inline-start: 0; + } + + &:where(:disabled, [data-disabled]) { + opacity: 0.4; + cursor: not-allowed; + } +} + +.control--default, +.control--contained { + &:where(:not(:disabled, [data-disabled])) { + @mixin hover { + @mixin where-light { + background-color: var(--mantine-color-gray-0); + } + + @mixin where-dark { + background-color: var(--mantine-color-dark-6); + } + } + } +} + +.label { + color: inherit; + font-weight: 400; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + padding-top: var(--mantine-spacing-sm); + padding-bottom: var(--mantine-spacing-sm); +} + +.chevron { + display: flex; + align-items: center; + justify-content: flex-start; + transition: transform var(--accordion-transition-duration, 200ms) ease; + width: var(--accordion-chevron-size, rem(15px)); + min-width: var(--accordion-chevron-size, rem(15px)); + transform: rotate(0deg); + + &:where([data-rotate]) { + transform: rotate(180deg); + } + + &:where([data-position="left"]) { + margin-inline-end: var(--mantine-spacing-md); + margin-inline-start: var(--mantine-spacing-md); + } +} + +.icon { + display: flex; + align-items: center; + justify-content: center; + margin-inline-end: var(--mantine-spacing-sm); + + &:where([data-chevron-position="left"]) { + margin-inline-end: 0; + margin-inline-start: var(--mantine-spacing-lg); + } +} + +.item { + @mixin where-light { + --item-border-color: var(--mantine-color-gray-3); + --item-filled-color: var(--mantine-color-gray-0); + } + + @mixin where-dark { + --item-border-color: var(--mantine-color-dark-4); + --item-filled-color: var(--mantine-color-dark-6); + } +} + +.item--default { + border-bottom: 1px solid var(--item-border-color); +} + +.item--contained { + border: 1px solid var(--item-border-color); + transition: background-color 150ms ease; + + &:where([data-active]) { + background-color: var(--item-filled-color); + } + + &:first-of-type { + border-start-start-radius: var(--accordion-radius); + border-start-end-radius: var(--accordion-radius); + + & > [data-accordion-control] { + border-start-start-radius: var(--accordion-radius); + border-start-end-radius: var(--accordion-radius); + } + } + + &:last-of-type { + border-end-start-radius: var(--accordion-radius); + border-end-end-radius: var(--accordion-radius); + + & > [data-accordion-control] { + border-end-start-radius: var(--accordion-radius); + border-end-end-radius: var(--accordion-radius); + } + } + + & + & { + border-top: 0; + } +} + +.item--filled { + border-radius: var(--accordion-radius); + + &:where([data-active]) { + background-color: var(--item-filled-color); + } +} + +.item--separated { + background-color: var(--item-filled-color); + border-radius: var(--accordion-radius); + border: 1px solid transparent; + transition: background-color 150ms ease; + + &[data-active] { + border-color: var(--item-border-color); + + @mixin where-light { + background-color: var(--mantine-color-white); + } + + @mixin where-dark { + background-color: var(--mantine-color-dark-7); + } + } + + & + & { + margin-top: var(--mantine-spacing-md); + } +} diff --git a/web/ui/mantine-ui/src/components/Accordion/Accordion.tsx b/web/ui/mantine-ui/src/components/Accordion/Accordion.tsx new file mode 100644 index 0000000000..220e242352 --- /dev/null +++ b/web/ui/mantine-ui/src/components/Accordion/Accordion.tsx @@ -0,0 +1,280 @@ +/* + * Some parts of this file are derived from Mantine UI (https://github.com/mantinedev/mantine) + * which is distributed under the MIT license: + * + * MIT License + * + * Copyright (c) 2021 Vitaly Rtishchev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Modifications to this file are licensed under the Apache License, Version 2.0. + */ + +import { useId, useUncontrolled } from "@mantine/hooks"; +import { + Box, + BoxProps, + createVarsResolver, + ElementProps, + ExtendComponent, + Factory, + getRadius, + getSafeId, + getWithProps, + MantineRadius, + MantineThemeComponent, + rem, + StylesApiProps, + useProps, + useStyles, +} from "@mantine/core"; +import { AccordionProvider } from "./Accordion.context"; +import { + AccordionChevronPosition, + AccordionHeadingOrder, + AccordionValue, +} from "./Accordion.types"; +import { AccordionChevron } from "./AccordionChevron"; +import { AccordionControl } from "./AccordionControl/AccordionControl"; +import { AccordionItem } from "./AccordionItem/AccordionItem"; +import { AccordionPanel } from "./AccordionPanel/AccordionPanel"; +import classes from "./Accordion.module.css"; + +export type AccordionStylesNames = + | "root" + | "content" + | "item" + | "panel" + | "icon" + | "chevron" + | "label" + | "itemTitle" + | "control"; + +export type AccordionVariant = "default" | "contained" | "filled" | "separated"; +export type AccordionCssVariables = { + root: + | "--accordion-transition-duration" + | "--accordion-chevron-size" + | "--accordion-radius"; +}; + +export interface AccordionProps + extends BoxProps, + StylesApiProps, + ElementProps<"div", "value" | "defaultValue" | "onChange"> { + /** If set, multiple items can be opened at the same time */ + multiple?: Multiple; + + /** Controlled component value */ + value?: AccordionValue; + + /** Uncontrolled component default value */ + defaultValue?: AccordionValue; + + /** Called when value changes, payload type depends on `multiple` prop */ + onChange?: (value: AccordionValue) => void; + + /** If set, arrow keys loop though items (first to last and last to first) @default `true` */ + loop?: boolean; + + /** Transition duration in ms @default `200` */ + transitionDuration?: number; + + /** If set, chevron rotation is disabled */ + disableChevronRotation?: boolean; + + /** Position of the chevron relative to the item label @default `right` */ + chevronPosition?: AccordionChevronPosition; + + /** Size of the chevron icon container @default `auto` */ + chevronSize?: number | string; + + /** Size of the default chevron icon. Ignored when `chevron` prop is set. @default `16` */ + chevronIconSize?: number | string; + + /** Heading order, has no effect on visuals */ + order?: AccordionHeadingOrder; + + /** Custom chevron icon */ + chevron?: React.ReactNode; + + /** Key of `theme.radius` or any valid CSS value to set border-radius. Numbers are converted to rem. @default `theme.defaultRadius` */ + radius?: MantineRadius; +} + +export type AccordionFactory = Factory<{ + props: AccordionProps; + ref: HTMLDivElement; + stylesNames: AccordionStylesNames; + vars: AccordionCssVariables; + variant: AccordionVariant; +}>; + +const defaultProps = { + multiple: false, + disableChevronRotation: false, + chevronPosition: "right", + variant: "default", + chevronSize: "auto", + chevronIconSize: 16, +} satisfies Partial; + +const varsResolver = createVarsResolver( + (_, { transitionDuration, chevronSize, radius }) => ({ + root: { + "--accordion-transition-duration": + transitionDuration === undefined + ? undefined + : `${transitionDuration}ms`, + "--accordion-chevron-size": + chevronSize === undefined ? undefined : rem(chevronSize), + "--accordion-radius": + radius === undefined ? undefined : getRadius(radius), + }, + }) +); + +export function Accordion( + _props: AccordionProps +) { + const props = useProps( + "Accordion", + defaultProps as AccordionProps, + _props + ); + const { + classNames, + className, + style, + styles, + unstyled, + vars, + children, + multiple, + value, + defaultValue, + onChange, + id, + loop, + transitionDuration, + disableChevronRotation, + chevronPosition, + chevronSize, + order, + chevron, + variant, + radius, + chevronIconSize, + attributes, + ...others + } = props; + + const uid = useId(id); + const [_value, handleChange] = useUncontrolled({ + value, + defaultValue, + finalValue: multiple ? ([] as any) : null, + onChange, + }); + + const isItemActive = (itemValue: string) => + Array.isArray(_value) ? _value.includes(itemValue) : itemValue === _value; + + const handleItemChange = (itemValue: string) => { + const nextValue: AccordionValue = Array.isArray(_value) + ? _value.includes(itemValue) + ? _value.filter((selectedValue) => selectedValue !== itemValue) + : [..._value, itemValue] + : itemValue === _value + ? null + : (itemValue as any); + + handleChange(nextValue); + }; + + const getStyles = useStyles({ + name: "Accordion", + classes, + props: props as AccordionProps, + className, + style, + classNames, + styles, + unstyled, + attributes, + vars, + varsResolver, + }); + + return ( + , + transitionDuration, + disableChevronRotation, + chevronPosition, + order, + loop, + getStyles, + variant, + unstyled, + }} + > + + {children} + + + ); +} + +const extendAccordion = ( + c: ExtendComponent +): MantineThemeComponent => c; + +Accordion.extend = extendAccordion; +Accordion.withProps = getWithProps( + Accordion as any +); +Accordion.classes = classes; +Accordion.displayName = "@mantine/core/Accordion"; +Accordion.Item = AccordionItem; +Accordion.Panel = AccordionPanel; +Accordion.Control = AccordionControl; +Accordion.Chevron = AccordionChevron; diff --git a/web/ui/mantine-ui/src/components/Accordion/Accordion.types.ts b/web/ui/mantine-ui/src/components/Accordion/Accordion.types.ts new file mode 100644 index 0000000000..e197d7ab45 --- /dev/null +++ b/web/ui/mantine-ui/src/components/Accordion/Accordion.types.ts @@ -0,0 +1,35 @@ +/* + * Some parts of this file are derived from Mantine UI (https://github.com/mantinedev/mantine) + * which is distributed under the MIT license: + * + * MIT License + * + * Copyright (c) 2021 Vitaly Rtishchev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Modifications to this file are licensed under the Apache License, Version 2.0. + */ + +export type AccordionValue = Multiple extends true + ? string[] + : string | null; + +export type AccordionHeadingOrder = 2 | 3 | 4 | 5 | 6; +export type AccordionChevronPosition = "left" | "right"; diff --git a/web/ui/mantine-ui/src/components/Accordion/AccordionChevron.tsx b/web/ui/mantine-ui/src/components/Accordion/AccordionChevron.tsx new file mode 100644 index 0000000000..95d746353f --- /dev/null +++ b/web/ui/mantine-ui/src/components/Accordion/AccordionChevron.tsx @@ -0,0 +1,66 @@ +/* + * Some parts of this file are derived from Mantine UI (https://github.com/mantinedev/mantine) + * which is distributed under the MIT license: + * + * MIT License + * + * Copyright (c) 2021 Vitaly Rtishchev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Modifications to this file are licensed under the Apache License, Version 2.0. + */ + +import { rem } from "@mantine/core"; + +export interface AccordionChevronProps + extends React.ComponentPropsWithoutRef<"svg"> { + /** Controls `width` and `height` of the icon, `16` by default */ + size?: number | string; +} + +export function AccordionChevron({ + style, + size = 16, + ...others +}: AccordionChevronProps) { + return ( + + + + ); +} + +AccordionChevron.displayName = "@mantine/core/AccordionChevron"; diff --git a/web/ui/mantine-ui/src/components/Accordion/AccordionControl/AccordionControl.tsx b/web/ui/mantine-ui/src/components/Accordion/AccordionControl/AccordionControl.tsx new file mode 100644 index 0000000000..57149d0b2e --- /dev/null +++ b/web/ui/mantine-ui/src/components/Accordion/AccordionControl/AccordionControl.tsx @@ -0,0 +1,175 @@ +/* + * Some parts of this file are derived from Mantine UI (https://github.com/mantinedev/mantine) + * which is distributed under the MIT license: + * + * MIT License + * + * Copyright (c) 2021 Vitaly Rtishchev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Modifications to this file are licensed under the Apache License, Version 2.0. + */ + +import { + Box, + BoxProps, + CompoundStylesApiProps, + createScopedKeydownHandler, + ElementProps, + factory, + Factory, + UnstyledButton, + useProps, +} from "@mantine/core"; +import { useAccordionContext } from "../Accordion.context"; +import { useAccordionItemContext } from "../AccordionItem.context"; +import classes from "../Accordion.module.css"; + +export type AccordionControlStylesNames = + | "control" + | "chevron" + | "label" + | "itemTitle" + | "icon"; + +export interface AccordionControlProps + extends BoxProps, + CompoundStylesApiProps, + ElementProps<"button"> { + /** Sets `disabled` attribute, prevents interactions */ + disabled?: boolean; + + /** Custom chevron icon */ + chevron?: React.ReactNode; + + /** Control label */ + children?: React.ReactNode; + + /** Icon displayed next to the label */ + icon?: React.ReactNode; +} + +export type AccordionControlFactory = Factory<{ + props: AccordionControlProps; + ref: HTMLButtonElement; + stylesNames: AccordionControlStylesNames; + compound: true; +}>; + +export const AccordionControl = factory( + (props, ref) => { + const { + classNames, + className, + style, + styles, + vars, + chevron, + icon, + onClick, + onKeyDown, + children, + disabled, + mod, + ...others + } = useProps("AccordionControl", null, props); + + const { value } = useAccordionItemContext(); + const ctx = useAccordionContext(); + const isActive = ctx.isItemActive(value); + const shouldWrapWithHeading = typeof ctx.order === "number"; + const Heading = `h${ctx.order!}` as const; + + const content = ( + + {...others} + {...ctx.getStyles("control", { + className, + classNames, + style, + styles, + variant: ctx.variant, + })} + unstyled={ctx.unstyled} + mod={[ + "accordion-control", + { + active: isActive, + "chevron-position": ctx.chevronPosition, + disabled, + }, + mod, + ]} + ref={ref} + onClick={(event) => { + onClick?.(event); + ctx.onChange(value); + }} + type="button" + disabled={disabled} + aria-expanded={isActive} + aria-controls={ctx.getRegionId(value)} + id={ctx.getControlId(value)} + onKeyDown={createScopedKeydownHandler({ + siblingSelector: "[data-accordion-control]", + parentSelector: "[data-accordion]", + activateOnFocus: false, + loop: ctx.loop, + orientation: "vertical", + onKeyDown, + })} + > + + {chevron || ctx.chevron} + + + {children} + + {icon && ( + + {icon} + + )} + + ); + + return shouldWrapWithHeading ? ( + + {content} + + ) : ( + content + ); + } +); + +AccordionControl.displayName = "@mantine/core/AccordionControl"; +AccordionControl.classes = classes; diff --git a/web/ui/mantine-ui/src/components/Accordion/AccordionItem.context.ts b/web/ui/mantine-ui/src/components/Accordion/AccordionItem.context.ts new file mode 100644 index 0000000000..af8420c070 --- /dev/null +++ b/web/ui/mantine-ui/src/components/Accordion/AccordionItem.context.ts @@ -0,0 +1,39 @@ +/* + * Some parts of this file are derived from Mantine UI (https://github.com/mantinedev/mantine) + * which is distributed under the MIT license: + * + * MIT License + * + * Copyright (c) 2021 Vitaly Rtishchev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Modifications to this file are licensed under the Apache License, Version 2.0. + */ + +import { createSafeContext } from "@mantine/core"; + +interface AccordionItemContext { + value: string; +} + +export const [AccordionItemProvider, useAccordionItemContext] = + createSafeContext( + "Accordion.Item component was not found in the tree" + ); diff --git a/web/ui/mantine-ui/src/components/Accordion/AccordionItem/AccordionItem.tsx b/web/ui/mantine-ui/src/components/Accordion/AccordionItem/AccordionItem.tsx new file mode 100644 index 0000000000..d0d9da6672 --- /dev/null +++ b/web/ui/mantine-ui/src/components/Accordion/AccordionItem/AccordionItem.tsx @@ -0,0 +1,84 @@ +/* + * Some parts of this file are derived from Mantine UI (https://github.com/mantinedev/mantine) + * which is distributed under the MIT license: + * + * MIT License + * + * Copyright (c) 2021 Vitaly Rtishchev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Modifications to this file are licensed under the Apache License, Version 2.0. + */ + +import { + Box, + BoxProps, + CompoundStylesApiProps, + ElementProps, + factory, + Factory, + useProps, +} from "@mantine/core"; +import { useAccordionContext } from "../Accordion.context"; +import { AccordionItemProvider } from "../AccordionItem.context"; +import classes from "../Accordion.module.css"; + +export type AccordionItemStylesNames = "item"; + +export interface AccordionItemProps + extends BoxProps, + CompoundStylesApiProps, + ElementProps<"div"> { + /** Value that is used to manage the accordion state */ + value: string; +} + +export type AccordionItemFactory = Factory<{ + props: AccordionItemProps; + ref: HTMLDivElement; + stylesNames: AccordionItemStylesNames; + compound: true; +}>; + +export const AccordionItem = factory((props, ref) => { + const { classNames, className, style, styles, vars, value, mod, ...others } = + useProps("AccordionItem", null, props); + const ctx = useAccordionContext(); + + return ( + + + + ); +}); + +AccordionItem.displayName = "@mantine/core/AccordionItem"; +AccordionItem.classes = classes; diff --git a/web/ui/mantine-ui/src/components/Accordion/AccordionPanel/AccordionPanel.tsx b/web/ui/mantine-ui/src/components/Accordion/AccordionPanel/AccordionPanel.tsx new file mode 100644 index 0000000000..d2c8baa788 --- /dev/null +++ b/web/ui/mantine-ui/src/components/Accordion/AccordionPanel/AccordionPanel.tsx @@ -0,0 +1,106 @@ +/* + * Some parts of this file are derived from Mantine UI (https://github.com/mantinedev/mantine) + * which is distributed under the MIT license: + * + * MIT License + * + * Copyright (c) 2021 Vitaly Rtishchev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Modifications to this file are licensed under the Apache License, Version 2.0. + */ + +import { + BoxProps, + Collapse, + CompoundStylesApiProps, + ElementProps, + factory, + Factory, + useProps, +} from "@mantine/core"; +import { useAccordionContext } from "../Accordion.context"; +import { useAccordionItemContext } from "../AccordionItem.context"; +import classes from "../Accordion.module.css"; +import { useEffect, useState } from "react"; + +export type AccordionPanelStylesNames = "panel" | "content"; + +export interface AccordionPanelProps + extends BoxProps, + CompoundStylesApiProps, + ElementProps<"div"> { + /** Called when the panel animation completes */ + onTransitionEnd?: () => void; +} + +export type AccordionPanelFactory = Factory<{ + props: AccordionPanelProps; + ref: HTMLDivElement; + stylesNames: AccordionPanelStylesNames; + compound: true; +}>; + +export const AccordionPanel = factory((props, ref) => { + const { classNames, className, style, styles, vars, children, ...others } = + useProps("AccordionPanel", null, props); + + const { value } = useAccordionItemContext(); + const ctx = useAccordionContext(); + + const isActive = ctx.isItemActive(value); + + // Prometheus-specific Accordion modification: unmount children when panel is closed. + const [showChildren, setShowChildren] = useState(isActive); + // Hide children from DOM 200ms after collapsing the panel + // to give the animation time to finish. + useEffect(() => { + let timeout: ReturnType; + + if (isActive) { + setShowChildren(true); + } else { + timeout = setTimeout(() => setShowChildren(false), 200); + } + + return () => clearTimeout(timeout); + }, [isActive]); + + return ( + +
+ {/* Prometheus-specific Accordion modification: unmount children when panel is closed. */} + {showChildren && children} +
+
+ ); +}); + +AccordionPanel.displayName = "@mantine/core/AccordionPanel"; +AccordionPanel.classes = classes; diff --git a/web/ui/mantine-ui/src/components/Accordion/index.ts b/web/ui/mantine-ui/src/components/Accordion/index.ts new file mode 100644 index 0000000000..aed1697ed3 --- /dev/null +++ b/web/ui/mantine-ui/src/components/Accordion/index.ts @@ -0,0 +1,47 @@ +/* + * Some parts of this file are derived from Mantine UI (https://github.com/mantinedev/mantine) + * which is distributed under the MIT license: + * + * MIT License + * + * Copyright (c) 2021 Vitaly Rtishchev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Modifications to this file are licensed under the Apache License, Version 2.0. + */ + +export { Accordion } from "./Accordion"; +export { AccordionChevron } from "./AccordionChevron"; +export { AccordionItem } from "./AccordionItem/AccordionItem"; +export { AccordionPanel } from "./AccordionPanel/AccordionPanel"; +export { AccordionControl } from "./AccordionControl/AccordionControl"; + +export type { + AccordionProps, + AccordionStylesNames, + AccordionCssVariables, + AccordionFactory, + AccordionVariant, +} from "./Accordion"; +export type { AccordionControlProps } from "./AccordionControl/AccordionControl"; +export type { AccordionItemProps } from "./AccordionItem/AccordionItem"; +export type { AccordionPanelProps } from "./AccordionPanel/AccordionPanel"; +export type { AccordionChevronProps } from "./AccordionChevron"; +export type { AccordionValue, AccordionHeadingOrder } from "./Accordion.types"; diff --git a/web/ui/mantine-ui/src/pages/AlertsPage.tsx b/web/ui/mantine-ui/src/pages/AlertsPage.tsx index 2608117c8f..ab46077cf8 100644 --- a/web/ui/mantine-ui/src/pages/AlertsPage.tsx +++ b/web/ui/mantine-ui/src/pages/AlertsPage.tsx @@ -3,7 +3,6 @@ import { Group, Table, Text, - Accordion, Badge, Tooltip, Box, @@ -38,6 +37,7 @@ import { KVSearch } from "@nexucis/kvsearch"; import { inputIconStyle } from "../styles"; import CustomInfiniteScroll from "../components/CustomInfiniteScroll"; import classes from "./AlertsPage.module.css"; +import { Accordion } from "../components/Accordion"; type AlertsPageData = { // How many rules are in each state across all groups. diff --git a/web/ui/mantine-ui/src/pages/RulesPage.tsx b/web/ui/mantine-ui/src/pages/RulesPage.tsx index 5f0bc7e5a8..bf95f20d35 100644 --- a/web/ui/mantine-ui/src/pages/RulesPage.tsx +++ b/web/ui/mantine-ui/src/pages/RulesPage.tsx @@ -1,5 +1,4 @@ import { - Accordion, Alert, Anchor, Badge, @@ -46,6 +45,7 @@ import classes from "./RulesPage.module.css"; import { useDebouncedValue, useLocalStorage } from "@mantine/hooks"; import { KVSearch } from "@nexucis/kvsearch"; import { StateMultiSelect } from "../components/StateMultiSelect"; +import { Accordion } from "../components/Accordion"; const kvSearch = new KVSearch({ shouldSort: true, diff --git a/web/ui/mantine-ui/src/pages/service-discovery/ServiceDiscoveryPoolsList.tsx b/web/ui/mantine-ui/src/pages/service-discovery/ServiceDiscoveryPoolsList.tsx index 4ba522d9da..a718646d5f 100644 --- a/web/ui/mantine-ui/src/pages/service-discovery/ServiceDiscoveryPoolsList.tsx +++ b/web/ui/mantine-ui/src/pages/service-discovery/ServiceDiscoveryPoolsList.tsx @@ -1,5 +1,4 @@ import { - Accordion, Alert, Anchor, Box, @@ -33,6 +32,7 @@ import { targetPoolDisplayLimit } from "./ServiceDiscoveryPage"; import { LabelBadges } from "../../components/LabelBadges"; import ErrorBoundary from "../../components/ErrorBoundary"; import RelabelSteps from "./RelabelSteps"; +import { Accordion } from "../../components/Accordion"; type TargetLabels = { discoveredLabels: Labels; diff --git a/web/ui/mantine-ui/src/pages/targets/ScrapePoolsList.tsx b/web/ui/mantine-ui/src/pages/targets/ScrapePoolsList.tsx index fbd710a412..cfbe8b237e 100644 --- a/web/ui/mantine-ui/src/pages/targets/ScrapePoolsList.tsx +++ b/web/ui/mantine-ui/src/pages/targets/ScrapePoolsList.tsx @@ -1,5 +1,4 @@ import { - Accordion, Alert, Anchor, Badge, @@ -10,10 +9,7 @@ import { Text, } from "@mantine/core"; import { KVSearch } from "@nexucis/kvsearch"; -import { - IconAlertTriangle, - IconInfoCircle, -} from "@tabler/icons-react"; +import { IconAlertTriangle, IconInfoCircle } from "@tabler/icons-react"; import { useSuspenseAPIQuery } from "../../api/api"; import { Target, TargetsResult } from "../../api/responseTypes/targets"; import React, { FC, memo, useMemo } from "react"; @@ -31,6 +27,7 @@ import panelClasses from "../../Panel.module.css"; import TargetLabels from "./TargetLabels"; import ScrapeTimingDetails from "./ScrapeTimingDetails"; import { targetPoolDisplayLimit } from "./TargetsPage"; +import { Accordion } from "../../components/Accordion"; type ScrapePool = { targets: Target[];