react#ElementType TypeScript Examples

The following examples show how to use react#ElementType. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: classed.ts    From apps with GNU Affero General Public License v3.0 7 votes vote down vote up
function classed<T, P extends Record<string, unknown>>(
  type: ElementType,
  ...className: string[]
): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> {
  return forwardRef<T, P>(function Classed(props, ref) {
    return React.createElement(type, {
      ...props,
      className: classNames(
        // eslint-disable-next-line react/prop-types
        props?.className,
        ...className,
      ),
      ref,
    });
  });
}
Example #2
Source File: ChangeLanguage.tsx    From hamagen-react-native with MIT License 6 votes vote down vote up
ChangeLanguage: ElementType = ({ locale, strings: { languages: { title } }, languages: { long }, changeLocale, toggleChangeLanguage }: Props) => {
  const onButtonPress = (selectedLocale: string) => {
    selectedLocale !== locale && changeLocale(selectedLocale);
    toggleChangeLanguage(false);
  };

  return (
    <>
      <View style={styles.titleWrapper}>
        <Text style={styles.title} bold>{title}</Text>
      </View>

      <ScrollView
        contentContainerStyle={{ alignItems: 'center' }}
        showsVerticalScrollIndicator={false}
      >
        {
            Object.keys(long).map((key: string, index: number) => (
              <TouchableOpacity key={index} onPress={() => onButtonPress(key)}>
                <View style={[styles.languageButton, key === locale && { backgroundColor: MAIN_COLOR }]}>
                  <Text style={[styles.text, key === locale && { color: '#fff' }]} black>{long[key]}</Text>
                </View>
              </TouchableOpacity>
            ))
          }
      </ScrollView>
    </>
  );
}
Example #3
Source File: ChangeLanguageButton.tsx    From hamagen-react-native with MIT License 6 votes vote down vote up
ChangeLanguageButton: ElementType = ({ locale, strings: { languages: { title } }, languages: { short, long }, toggleChangeLanguage }: Props) => {
  return (
    <TouchableOpacity
      onPress={() => toggleChangeLanguage(true)}
      importantForAccessibility="no-hide-descendants"
      accessibilityElementsHidden
      accessibilityHint={long[locale]}
      accessibilityLabel={title}
    >
      <View style={styles.container}>
        <Text style={styles.text}>{short[locale]}</Text>
        <Icon source={require('../../assets/onboarding/lang.png')} width={20} />
      </View>
    </TouchableOpacity>
  );
}
Example #4
Source File: JoyTokenIcon.tsx    From atlas with GNU General Public License v3.0 6 votes vote down vote up
VARIANT_SIZE_COMPONENT_MAPPING: Record<JoyTokenIconVariant, Record<JoyTokenIconSize, ElementType>> = {
  primary: {
    16: SvgJoyTokenPrimary16,
    24: SvgJoyTokenPrimary24,
    32: SvgJoyTokenPrimary32,
    48: SvgJoyTokenPrimary48,
  },
  silver: {
    16: SvgJoyTokenSilver16,
    24: SvgJoyTokenSilver24,
    32: SvgJoyTokenSilver32,
    48: SvgJoyTokenSilver48,
  },
  regular: {
    16: SvgJoyTokenMonochrome16,
    24: SvgJoyTokenMonochrome24,
    32: SvgJoyTokenMonochrome32,
    48: SvgJoyTokenMonochrome48,
  },
  gray: {
    16: SvgJoyTokenMonochrome16,
    24: SvgJoyTokenMonochrome24,
    32: SvgJoyTokenMonochrome32,
    48: SvgJoyTokenMonochrome48,
  },
}
Example #5
Source File: CodeEditor.tsx    From hub with Apache License 2.0 6 votes vote down vote up
CodeEditor: ElementType = (props: Props) => {
  const { ctx } = useContext(AppCtx);
  const { effective } = ctx.prefs.theme;

  const isDisabled = !isUndefined(props.disabled) && props.disabled;

  return (
    <CodeMirror
      className={classnames('border position-relative h-100', styles.code, { [styles.disabled]: isDisabled })}
      value={props.value}
      options={{
        mode: {
          name: props.mode,
          json: true,
          statementIndent: 2,
        },
        theme: effective === 'dark' ? 'material-darker' : 'elegant',
        lineNumbers: true,
        inputStyle: 'contenteditable',
        viewportMargin: Infinity,
        readOnly: isDisabled ? 'nocursor' : false,
        tabindex: 0,
      }}
      editorDidMount={(editor) => {
        editor.setSize('', '100%');
      }}
      onBeforeChange={(editor: any, data: any, value: string) => {
        props.onChange(value);
      }}
    />
  );
}
Example #6
Source File: Button.tsx    From pancake-toolkit with GNU General Public License v3.0 6 votes vote down vote up
Button = <E extends ElementType = "button">(props: ButtonProps<E>): JSX.Element => {
  const { startIcon, endIcon, external, className, isLoading, disabled, children, ...rest } = props;
  const internalProps = external ? getExternalLinkProps() : {};
  const isDisabled = isLoading || disabled;
  const classNames = className ? [className] : [];

  if (isLoading) {
    classNames.push("pancake-button--loading");
  }

  if (isDisabled && !isLoading) {
    classNames.push("pancake-button--disabled");
  }

  return (
    <StyledButton
      $isLoading={isLoading}
      className={classNames.join(" ")}
      disabled={isDisabled}
      {...internalProps}
      {...rest}
    >
      <>
        {isValidElement(startIcon) &&
          cloneElement(startIcon, {
            mr: "0.5rem",
          })}
        {children}
        {isValidElement(endIcon) &&
          cloneElement(endIcon, {
            ml: "0.5rem",
          })}
      </>
    </StyledButton>
  );
}
Example #7
Source File: ParamInfo.tsx    From hub with Apache License 2.0 6 votes vote down vote up
Link: ElementType = (data: LinkProps) => {
  const linkIcon =
    data.children && isArray(data.children) && data.children[0] === 'iconLink' ? (
      <FiExternalLink className={`position-relative ${styles.linkIcon}`} />
    ) : undefined;

  return (
    <a href={data.href} target={data.target} rel="noopener noreferrer" className="d-inline-block text-dark">
      {linkIcon || data.children}
    </a>
  );
}
Example #8
Source File: index.tsx    From engine with MIT License 6 votes vote down vote up
join = (...args: ProducersAndViews[]) => {
  //@ts-ignore
  const elements = flattenDeep(args);
  const views = extractViews(args);
  const producers = extractProducers(args);
  const components = elements.filter(
    (x: unknown) => !isView(x) && isValidElementType(x)
  ) as ElementType[];

  if (views.length === 0 && components.length === 0 && producers.length === 0) {
    console.error(
      "Component creation failed using join. Please provide at least view, producer or react component"
    );
    return <></>;
  }
  if (views.length === 1 && components.length === 0) {
    const view = views[0];
    if (producers.length > 0) {
      //@ts-ignore
      view.producers(producers);
    }
    return view;
  } else {
    const list: (ElementType | View)[] = [...components, ...views];
    const Component: view = ({ _props }: { _props: unknown }) => {
      return (
        <>
          {list.map((X, i) => (
            <X {..._props} key={i} />
          ))}
        </>
      );
    };
    Component.producers(producers);
    return Component;
  }
}
Example #9
Source File: Button.tsx    From ui with MIT License 6 votes vote down vote up
export function Button<T extends ElementType = "button">({
  as,
  intent = "secondary",
  size = "standard",
  fluid,
  children,
  className,
  ...rest
}: ButtonProps<T>) {
  const Element: ElementType = as;
  const classNames = cx(
    "button",
    objectToClassnames({ intent, size }),
    { fluid },
    className,
  );

  if (children !== undefined) {
    return (
      <Element className={classNames} {...rest}>
        {children}
      </Element>
    );
  }

  return null;
}
Example #10
Source File: Field.tsx    From ke with MIT License 6 votes vote down vote up
Field = makeWithLayout(
  ({ name, as, validator, label, isRequired, ...other }: FieldProps<unknown, ControlProps<unknown>>) => {
    const controlRef = useRef<ControlRefProps>(null)
    const { value, onChange } = useField(name, controlRef)
    const { errors, validate } = useFieldValidation(name, value, validator)

    const handleChange = useCallback(
      async (v: unknown) => {
        await validate(v)
        onChange(v)
      },
      [onChange, validate]
    )

    // Don't found why, but type declaration necessary here https://github.com/microsoft/TypeScript/issues/28631#issuecomment-477240245
    const Component: ElementType = as

    return {
      // Это обёртка
      // eslint-disable-next-line react/jsx-props-no-spreading
      Control: <Component ref={controlRef} value={value} onChange={handleChange} name={name} {...other} />,
      Errors: errors && errors.length ? errors[0].message : '',
      Label: label && (
        <Label isRequired={isRequired} mb={2} display="inline-block">
          {label}
        </Label>
      ),
    }
  },
  Simple
) as <T, P extends ControlProps<T>>(
  props: PropsWithDefaultLayout<FieldProps<T, P>, { Control: JSX.Element }>
) => JSX.Element
Example #11
Source File: withPasswordProtect.tsx    From next-password-protect with MIT License 5 votes vote down vote up
withPasswordProtect = (
  App: any,
  options?: PasswordProtectHOCOptions,
) => {
  const ProtectedApp = ({ Component, pageProps, ...props }: AppProps) => {
    const isAmp = useAmp();
    const [isAuthenticated, setAuthenticated] = useState<undefined | boolean>(
      undefined,
    );

    const checkIfLoggedIn = async () => {
      try {
        const res = await fetch(options?.checkApiUrl || '/api/passwordCheck', {
          credentials: 'include',
        });

        if (res.status === 200) {
          setAuthenticated(true);
        } else {
          setAuthenticated(false);
        }
      } catch (e) {
        setAuthenticated(false);
      }
    };

    useEffect(() => {
      checkIfLoggedIn();
    }, []);

    if (isAuthenticated === undefined) {
      return null;
    }

    if (isAuthenticated) {
      return <App Component={Component} pageProps={pageProps} {...props} />;
    }

    // AMP is not yet supported
    if (isAmp) {
      return null;
    }

    const LoginComponent: ElementType =
      options?.loginComponent || DefaultLoginComponent;

    return (
      <LoginComponent
        apiUrl={options?.loginApiUrl}
        {...(options?.loginComponentProps || {})}
      />
    );
  };

  return ProtectedApp;
}
Example #12
Source File: context.ts    From pancake-toolkit with GNU General Public License v3.0 5 votes vote down vote up
MenuContext = createContext<{ linkComponent: ElementType }>({ linkComponent: "a" })
Example #13
Source File: index.tsx    From fower with MIT License 5 votes vote down vote up
/**
 * style any Component
 * @param component tag name or React Component
 * @param args
 *
 * @example
 *
 * ```jsx
 * styled(Button)
 *
 * styled(Button, ['textXL', 'fontBold', 'textCenter'])
 *
 * styled(Button, {
 *    p: 8,
 *   color: 'red'
 * })
 *
 * styled(Button, ['textXL', 'fontBold', 'textCenter'], { color: 'red' })
 *
 * ```
 */
export function styled<C extends keyof JSX.IntrinsicElements | ElementType>(
  component: C,
  ...args: StyledArgs
): StyledComponent<
  JSX.LibraryManagedAttributes<C, ComponentProps<C>> & AtomicProps & InjectedProps
> {
  if (!component) return null as any

  const StyledComponent = forwardRef((props = {}, ref) => {
    const { inline } = store.config
    const prepareProps = { ...argsToProps(args, store.config?.objectPropKeys?.[0]), ...props }

    const parsedProps = inline
      ? getInLineParsedProps(prepareProps)
      : getCssParsedProps(prepareProps)
    return createElement(component, { ref, ...parsedProps })
  })

  StyledComponent.displayName =
    typeof component === 'string' ? `Styled${upFirst(component as string)}` : 'StyledComponent'

  hoistNonReactStatics(StyledComponent, component as any)
  return StyledComponent
}
Example #14
Source File: Tag.tsx    From webapis-playground with MIT License 5 votes vote down vote up
Tag = <Element extends ElementType = 'span'>({
  children,
  as,
  className,
  leftIcon,
  rightIcon,
  ...props
}: TagProps<Element> & ComponentPropsWithoutRef<Element>) => {
  let Component = as || 'span';

  return (
    <Component
      className={cx(
        `
          tw-inline-flex
          tw-items-center
          tw-justify-center
          tw-py-1
          tw-px-2
          tw-text-xs
          tw-font-bold 
          tw-text-white
          tw-whitespace-nowrap
          tw-uppercase
          tw-rounded-md 
          tw-bg-gray-500
          tw-transition
          tw-duration-100
          tw-ease-in
          tw-appearance-none
          tw-select-none
          tw-align-middle
          active:tw-bg-gray-800
          hover:tw-bg-gray-600
        `,
        className
      )}
      {...filterDOMProps(props)}
    >
      {leftIcon && (
        <span className="tw-inline-flex tw-self-center tw-flex-shrink-0 tw-mr-1">
          {leftIcon}
        </span>
      )}
      {children}
      {rightIcon && (
        <span className="tw-inline-flex tw-self-center tw-flex-shrink-0 tw-ml-1">
          {rightIcon}
        </span>
      )}
    </Component>
  );
}
Example #15
Source File: IconButton.tsx    From webapis-playground with MIT License 5 votes vote down vote up
IconButton = <Element extends ElementType = 'button'>({
  children,
  icon,
  as,
  className,
  ...props
}: IconButtonProps<Element> & ComponentPropsWithoutRef<Element>) => {
  let element = children || icon;

  const child = React.isValidElement(element)
    ? React.cloneElement(element as any, {
        'aria-hidden': true,
        focusable: false,
      })
    : null;

  let Component = as || 'button';

  return (
    <Component
      className={cx(
        `
          tw-inline-flex
          tw-items-center
          tw-justify-center
          tw-px-3
          tw-h-10
          tw-text-base
          tw-font-semibold 
          tw-text-gray-500
          tw-whitespace-nowrap
          tw-rounded-md 
          tw-bg-transparent
          tw-transition
          tw-duration-200
          tw-ease-in
          tw-appearance-none
          tw-select-none
          tw-align-middle
          focus:tw-ring-4
          focus:tw-ring-blue-200
          focus:tw-outline-none
          active:tw-bg-gray-200
          hover:tw-bg-gray-100
          disabled:tw-opacity-50
          disabled:tw-pointer-events-none
          disabled:tw-select-none
        `,
        className
      )}
      {...filterDOMProps(props)}
    >
      <span className="tw-inline-flex tw-self-center tw-flex-shrink-0">
        {child}
      </span>
    </Component>
  );
}
Example #16
Source File: Button.tsx    From webapis-playground with MIT License 5 votes vote down vote up
Button = <Element extends ElementType = 'button'>({
  children,
  as,
  className,
  leftIcon,
  rightIcon,
  ...props
}: ButtonProps<Element> & ComponentPropsWithoutRef<Element>) => {
  let Component = as || 'button';

  return (
    <Component
      className={cx(
        `
          tw-inline-flex
          tw-items-center
          tw-justify-center
          tw-px-4
          tw-h-10
          tw-text-base
          tw-font-semibold 
          tw-text-white
          tw-whitespace-nowrap
          tw-rounded-md 
          tw-bg-blue-500
          tw-transition
          tw-duration-100
          tw-ease-in
          tw-appearance-none
          tw-select-none
          tw-align-middle
          focus:tw-ring-4
          focus:tw-ring-blue-300
          focus:tw-outline-none
          active:tw-bg-blue-800
          hover:tw-bg-blue-600
          disabled:tw-opacity-50
          disabled:tw-pointer-events-none
          disabled:tw-select-none
        `,
        className
      )}
      {...filterDOMProps(props)}
    >
      {leftIcon && (
        <span className="tw-inline-flex tw-self-center tw-flex-shrink-0 tw-mr-2">
          {leftIcon}
        </span>
      )}
      {children}
      {rightIcon && (
        <span className="tw-inline-flex tw-self-center tw-flex-shrink-0 tw-ml-2">
          {rightIcon}
        </span>
      )}
    </Component>
  );
}
Example #17
Source File: ParamInfo.tsx    From hub with Apache License 2.0 5 votes vote down vote up
Heading: ElementType = (data: HeadingProps) => {
  const Tag = `h${data.level}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
  return <Tag className={`text-dark lh-1 fw-bold ${styles.header}`}>{data.children}</Tag>;
}
Example #18
Source File: index.tsx    From hub with Apache License 2.0 5 votes vote down vote up
Heading: ElementType = (data: HeadingProps) => {
  const Tag = `h${data.level}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
  return <Tag className={`text-dark lh-1 fw-bold ${styles.header}`}>{data.children}</Tag>;
}
Example #19
Source File: AnchorHeader.tsx    From hub with Apache License 2.0 5 votes vote down vote up
AnchorHeader: ElementType = (props: Props) => {
  let value = props.title;
  if (isUndefined(value) && props.children && props.children.length > 0) {
    const allContentValues = props.children.map((n: any) => {
      if (isString(n)) {
        return [n];
      } else if (isObject(n)) {
        return String((n as any).props.children);
      } else {
        return '';
      }
    });
    value = allContentValues.join('');
  }

  if (isUndefined(value)) return null;

  const Tag = `h${props.level}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
  const anchor = props.anchorName || getAnchorValue(value);

  return (
    <span className={styles.header}>
      <Tag className={`position-relative anchorHeader ${styles.headingWrapper} ${props.className}`}>
        <div data-testid="anchor" className={`position-absolute ${styles.headerAnchor}`} id={anchor} />
        <a
          href={`${history.location.pathname}#${anchor}`}
          onClick={(e: ReactMouseEvent<HTMLAnchorElement, MouseEvent>) => {
            e.preventDefault();
            e.stopPropagation();
            props.scrollIntoView(`#${anchor}`);
          }}
          role="button"
          className={`text-reset text-center d-none d-md-block lh-1 float-start ${styles.headingLink}`}
          aria-label={value}
        >
          <GoLink />
        </a>
        {props.title || props.children}
      </Tag>
    </span>
  );
}
Example #20
Source File: Alert.tsx    From hub with Apache License 2.0 5 votes vote down vote up
Alert: ElementType = (props: Props) => {
  const [errorMessage, setErrorMessage] = useState<string | null>(null);
  const errorWrapper = useRef<HTMLDivElement>(null);
  const [isVisible, setIsVisible] = useState<boolean>(false);

  useEffect(() => {
    let timeout: NodeJS.Timeout;
    if (isNull(props.message)) {
      if (!isNull(errorMessage)) {
        setIsVisible(false);
        timeout = setTimeout(() => {
          setErrorMessage(null);
        }, 1000);
      }
    } else {
      if (props.message !== errorMessage) {
        setErrorMessage(props.message);
        setIsVisible(true);
        errorWrapper.current!.scrollIntoView({ block: 'start', inline: 'nearest', behavior: 'smooth' });
      }
    }

    return () => {
      if (!isUndefined(timeout)) {
        clearTimeout(timeout);
      }
    };
  }, [props.message]); /* eslint-disable-line react-hooks/exhaustive-deps */

  return (
    <div
      data-testid="alertWrapper"
      className={classnames('overflow-hidden', styles.alertWrapper, { [styles.isAlertActive]: isVisible })}
      ref={errorWrapper}
    >
      {isVisible && (
        <div className={`alert alert-${props.type || DEFAULT_ALERT_TYPE} mt-3 mb-0`} role="alert">
          <div className="d-flex flex-row align-items-start justify-content-between">
            <div>{errorMessage || ''}</div>
            {!isUndefined(props.onClose) && (
              <button
                data-testid="closeAlertBtn"
                type="button"
                className="btn-close ms-3"
                onClick={props.onClose}
                aria-label="Close alert"
              ></button>
            )}
          </div>
        </div>
      )}
    </div>
  );
}
Example #21
Source File: Readme.tsx    From hub with Apache License 2.0 4 votes vote down vote up
Readme = (props: Props) => {
  const Code: ElementType = ({ inline, className, children }: CodeProps) => {
    const match = /language-(\w+)/.exec(className || '');
    if (inline) {
      return <code className={className}>{children}</code>;
    } else {
      return (
        <SyntaxHighlighter language={checkCodeLanguage(match ? match[1] : 'bash')} style={github}>
          {String(children).replace(/\n$/, '')}
        </SyntaxHighlighter>
      );
    }
  };

  const Image: ElementType = (data: ImageProps) => {
    const img = useRef<HTMLImageElement>(null);
    const [error, setError] = useState<boolean>(false);
    const [isBigImage, setIsBigImage] = useState<boolean>(false);
    const point = useBreakpointDetect();

    const checkImageInBreakpoint = useCallback(() => {
      if (!isNull(img) && img.current && point && ['md', 'lg', 'xl'].includes(point) && img.current!.width > 410) {
        setIsBigImage(true);
      }
    }, [point]);

    useEffect(() => {
      checkImageInBreakpoint();
    }, [point, checkImageInBreakpoint]);

    return /^https?:/.test(data.src) ? (
      <span className={classnames({ 'overflow-hidden d-table-cell': isBigImage })}>
        <img
          ref={img}
          src={data.src}
          alt={data.alt || ''}
          className={classnames({ 'd-none': error })}
          onError={() => setError(true)}
          onLoad={checkImageInBreakpoint}
        />
      </span>
    ) : null;
  };

  // Only for external links and anchors
  const Link: ElementType = (data: LinkProps) => {
    const isContentImage =
      data.children && isArray(data.children) && data.children.length > 0 && !isUndefined(data.children[0].props)
        ? !isUndefined(data.children[0].props.src)
        : false;

    if (/^https?:/.test(data.href)) {
      return (
        // We need to force display inline when content is not an image due to
        // .paragraph a:only-child {
        //   display: table-cell;
        // }
        <a
          href={data.href}
          target={data.target}
          rel="noopener noreferrer"
          className={classnames('text-primary', { 'd-inline': !isContentImage })}
        >
          {data.children}
        </a>
      );
      // We only displays anchors when title is on the Readme
    } else if (data.href.startsWith('#') && isElementInView(data.href)) {
      return (
        <button
          className={classnames('btn btn-link text-primary text-start border-0 p-0', styles.btnLink)}
          onClick={() => props.scrollIntoView(data.href)}
          aria-label="Go to element"
        >
          {data.children}
        </button>
      );
    } else {
      return <>{data.children}</>;
    }
  };

  const Table: ElementType = (data: BasicProps) => (
    <div className="mw-100 overflow-auto">
      <table>{data.children}</table>
    </div>
  );

  const Paragraph: ElementType = (data: BasicProps) => {
    const isOneChild = data.children && isArray(data.children) && data.children.length === 1;
    if (isUndefined(data.children)) return null;
    return <p className={classnames({ 'd-block w-100 h-100': isOneChild }, styles.paragraph)}>{data.children}</p>;
  };

  const Blockquote: ElementType = (data: BasicProps) => {
    return <blockquote className={`text-muted position-relative ${styles.quote}`}>{data.children}</blockquote>;
  };

  const Heading: ElementType = (data: any) => <AnchorHeader {...data} scrollIntoView={props.scrollIntoView} />;

  const isElementInView = (id: string) => {
    try {
      const item = document.querySelector(id);
      return !isNull(item);
    } catch {
      return false;
    }
  };

  const Pre: ElementType = (props: CodeProps) => {
    return <>{props.children}</>;
  };

  return (
    <ReactMarkdown
      className={`mt-3 mb-5 position-relative ${styles.md}`}
      children={props.readme}
      linkTarget="_blank"
      skipHtml
      remarkPlugins={[[remarkGfm, { tableCellPadding: false }]]}
      components={{
        pre: Pre,
        code: Code,
        image: Image,
        img: Image,
        a: Link,
        table: Table,
        h1: Heading,
        h2: Heading,
        h3: Heading,
        h4: Heading,
        h5: Heading,
        h6: Heading,
        p: Paragraph,
        blockquote: Blockquote,
      }}
    />
  );
}
Example #22
Source File: PublisherInstructionsInstall.tsx    From hub with Apache License 2.0 4 votes vote down vote up
PublisherInstructionsInstall = (props: Props) => {
  const Code: ElementType = (props: CodeProps) => {
    if (props.inline) {
      return <code className={`border ${styles.inlineCode}`}>{props.children}</code>;
    }
    if (props.children) {
      const content = String(props.children).replace(/\n$/, '');
      return <CommandBlock command={content} />;
    } else {
      return null;
    }
  };

  const Pre: ElementType = (props: CodeProps) => {
    return <>{props.children}</>;
  };

  const Heading: ElementType = (props: HeadingProps) => (
    <div className="my-2">
      <div className={`h${props.level} text-muted pt-2 pb-1`}>
        <div className={styles.mdHeader}>{props.children}</div>
      </div>
    </div>
  );

  const Table: ElementType = (data: TableProps) => (
    <div className="w-100 overflow-auto">
      <table>{data.children}</table>
    </div>
  );

  const Image: ElementType = (data: ImageProps) => {
    const [error, setError] = useState<boolean>(false);

    // Only absolute path
    return /^https?:/.test(data.src) ? (
      <img
        src={data.src}
        alt={data.alt || ''}
        className={classnames('mw-100', { 'd-none': error })}
        onError={() => setError(true)}
      />
    ) : null;
  };

  // Only for external links and anchors
  const Link: ElementType = (data: LinkProps) => {
    // Only absolute link
    return /^https?:/.test(data.href) ? (
      <a href={data.href} target={data.target} rel="noopener noreferrer" className="text-primary">
        {data.children}
      </a>
    ) : null;
  };

  return (
    <ErrorBoundary message="Something went wrong rendering the install instructions of this package.">
      <span data-testid="readme">
        <ReactMarkdown
          className={`mt-3 mb-5 ${styles.md}`}
          children={props.install}
          linkTarget="_blank"
          remarkPlugins={[[remarkGfm, { singleTilde: false }]]}
          skipHtml
          components={{
            pre: Pre,
            code: Code,
            img: Image,
            a: Link,
            h1: Heading,
            h2: Heading,
            h3: Heading,
            h4: Heading,
            h5: Heading,
            h6: Heading,
            table: Table,
          }}
        />
      </span>
    </ErrorBoundary>
  );
}
Example #23
Source File: index.tsx    From hub with Apache License 2.0 4 votes vote down vote up
UserNotificationsController: ElementType = () => {
  const { dispatch, ctx } = useContext(AppCtx);
  const [notification, setNotification] = useState<UserNotification | null>(null);
  const [isVisible, setIsVisible] = useState<boolean>(false);
  const [addNotificationTimeout, setAddNotificationTimeout] = useState<NodeJS.Timeout | undefined>(undefined);
  const [dismissNotificationTimeout, setDismissNotificationTimeout] = useState<NodeJS.Timeout | undefined>(undefined);
  const point = useBreakpointDetect();

  notificationsDispatcher.subscribe({
    updateUserNotificationsWrapper: (notif: UserNotification | null) => {
      if (!isNull(notif)) {
        setNotification(notif);
        setIsVisible(true);
        setAddNotificationTimeout(
          setTimeout(() => {
            dispatch(addNewDisplayedNotification(notif.id));
          }, ANIMATION_TIME)
        );
      } else {
        setIsVisible(false);
        setDismissNotificationTimeout(
          setTimeout(() => {
            if (!isNull(notification)) {
              setNotification(null);
            }
          }, ANIMATION_TIME)
        );
      }
    },
  });

  const onClose = () => {
    notificationsDispatcher.dismissNotification();
  };

  const onChangeNotificationsPrefs = () => {
    notificationsDispatcher.dismissNotification();
    // Change user prefs
    dispatch(enabledDisplayedNotifications(false));
  };

  useEffect(() => {
    if (!isUndefined(ctx.user)) {
      notificationsDispatcher.start(ctx.prefs.notifications, point);
    }
    return () => {
      notificationsDispatcher.close();
    };
  }, [ctx.user]); /* eslint-disable-line react-hooks/exhaustive-deps */

  useEffect(() => {
    if (!isUndefined(ctx.user)) {
      notificationsDispatcher.updateSettings(ctx.prefs.notifications);
    }
  }, [ctx.prefs.notifications]); /* eslint-disable-line react-hooks/exhaustive-deps */

  useEffect(() => {
    return () => {
      if (addNotificationTimeout) {
        clearTimeout(addNotificationTimeout);
      }
      if (dismissNotificationTimeout) {
        clearTimeout(dismissNotificationTimeout);
      }
    };
  }, [addNotificationTimeout, dismissNotificationTimeout]);

  return (
    <div className="d-none d-md-flex justify-content-center align-items-center w-100">
      <div
        className={classnames(
          'position-fixed toast fade border border-3 bg-white opacity-0',
          styles.toast,
          {
            [`show opacity-100 ${styles.isVisible}`]: !isNull(notification) && isVisible,
          },
          'notificationCard'
        )}
        role="alert"
        aria-live="assertive"
        aria-atomic="true"
      >
        {!isNull(notification) && (
          <div className="toast-body" data-testid="notificationContent">
            <div>
              <div className={`float-end ${styles.btnsWrapper}`}>
                <div className="d-flex flex-row align-items-center">
                  <button
                    type="button"
                    className={`btn btn-link text-dark py-0 ${styles.btn}`}
                    onClick={onChangeNotificationsPrefs}
                    aria-label="Disable usage tips"
                  >
                    Don't show me more again
                  </button>
                  <button
                    type="button"
                    className={`btn-close ${styles.closeBtn}`}
                    onClick={onClose}
                    aria-label="Close"
                  ></button>
                </div>
              </div>
              <span>
                <ReactMarkdown
                  className={styles.content}
                  children={notification.body}
                  components={{
                    h1: Heading,
                    h2: Heading,
                    h3: Heading,
                    h4: Heading,
                    h5: Heading,
                    h6: Heading,
                  }}
                  linkTarget="_blank"
                  skipHtml
                />
              </span>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}