use-debounce#useDebounce TypeScript Examples

The following examples show how to use use-debounce#useDebounce. 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: FormAutosave.tsx    From firetable with Apache License 2.0 6 votes vote down vote up
export default function FormAutosave({ control, handleSave }: IAutosaveProps) {
  const values = useWatch({ control });

  const [debouncedValue] = useDebounce(values, 1000, {
    equalityFn: _isEqual,
  });

  useEffect(() => {
    handleSave(debouncedValue);
  }, [debouncedValue]);

  return null;
}
Example #2
Source File: Tooltip.tsx    From posthog-foss with MIT License 6 votes vote down vote up
/** Extension of Ant Design's Tooltip that enables a delay.
 *
 * Caveat: doesn't work with disabled elements due to lack of workaround that Ant Design uses.
 * See https://github.com/ant-design/ant-design/blob/master/components/tooltip/index.tsx#L82-L130.
 */
// CAUTION: Any changes here will affect tooltips across the entire app.
export function Tooltip({
    children,
    visible,
    isDefaultTooltip = false,
    delayMs = DEFAULT_DELAY_MS,
    ...props
}: TooltipProps): JSX.Element {
    const [localVisible, setVisible] = useState(visible)
    const [debouncedLocalVisible] = useDebounce(visible ?? localVisible, delayMs)

    if (!isDefaultTooltip && !('mouseEnterDelay' in props)) {
        // If not preserving default behavior and mouseEnterDelay is not already provided, we use a custom default here
        props.mouseEnterDelay = delayMs
    }

    // If child is not a valid element (string or string + ReactNode, Fragment), antd wraps children in a span.
    // See https://github.com/ant-design/ant-design/blob/master/components/tooltip/index.tsx#L226
    const child = React.isValidElement(children) ? children : <span>{children}</span>

    return props.title ? (
        <AntdTooltip {...props} visible={isDefaultTooltip ? visible : localVisible && debouncedLocalVisible}>
            {React.cloneElement(child, {
                onMouseEnter: () => setVisible(true),
                onMouseLeave: () => setVisible(false),
            })}
        </AntdTooltip>
    ) : (
        child
    )
}
Example #3
Source File: Autosave.tsx    From firetable with Apache License 2.0 5 votes vote down vote up
export default function Autosave({
  control,
  docRef,
  row,
  reset,
  dirtyFields,
}: IAutosaveProps) {
  const { tableState, updateCell } = useFiretableContext();

  const values = useWatch({ control });
  const [debouncedValue] = useDebounce(getEditables(values, tableState), 1000, {
    equalityFn: _isEqual,
  });

  useEffect(() => {
    if (!row || !row.ref) return;
    if (row.ref.id !== docRef.id) return;
    if (!updateCell) return;

    // Get only fields that have had their value updated by the user
    const updatedValues = _pickBy(
      _pickBy(debouncedValue, (_, key) => dirtyFields[key]),
      (value, key) => !_isEqual(value, row[key])
    );
    console.log(debouncedValue, row);
    console.log(updatedValues, dirtyFields);
    if (Object.keys(updatedValues).length === 0) return;

    // Update the document
    Object.entries(updatedValues).forEach(([key, value]) =>
      updateCell(
        row.ref,
        key,
        value,
        // After the cell is updated, set this field to be not dirty
        // so it doesn’t get updated again when a different field in the form
        // is updated + make sure the new value is kept after reset
        () => reset({ ...values, [key]: value })
      )
    );
  }, [debouncedValue]);

  return null;
}
Example #4
Source File: ObjectsPanel.tsx    From react-design-editor with MIT License 5 votes vote down vote up
function ObjectsPanel() {
  const [search, setSearch] = useState('')
  const [objects, setObjects] = useState<any[]>([])
  const [value] = useDebounce(search, 1000)
  const { canvas } = useCanvasContext()

  useEffect(() => {
    getImages('love')
      .then((data: any) => setObjects(data))
      .catch(console.log)
  }, [])

  useEffect(() => {
    if (value) {
      getImages(value)
        .then((data: any) => setObjects(data))
        .catch(console.log)
    }
  }, [value])
  const renderItems = () => {
    return objects.map(obj => {
      return (
        <div className="object-item-container" onClick={() => downloadImage(obj.uuid)} key={obj.uuid}>
          <img className="object-item" src={obj.urls.thumb} />
        </div>
      )
    })
  }
  const downloadImage = uuid => {
    getImage(uuid)
      .then(url => {
        fabric.loadSVGFromURL(url, (objects, options) => {
          const object = fabric.util.groupSVGElements(objects, options)
          //@ts-ignore
          const workarea = canvas.getObjects().find(obj => obj.id === 'workarea')
          canvas.add(object)
          object.scaleToHeight(300)
          object.center()
          object.clipPath = workarea
          canvas.renderAll()
        })
      })
      .catch(console.log)
  }

  return (
    <>
      <div style={{ padding: '1rem 2rem' }}>
        <InputGroup>
          <InputLeftElement pointerEvents="none" children={<SearchIcon color="gray.300" />} />
          <Input
            onChange={e => setSearch(e.target.value)}
            style={{ background: '#fff' }}
            type="tel"
            placeholder="Search objects"
          />
        </InputGroup>
      </div>
      <div style={{ padding: '0 2rem' }} className="objects-list">
        {renderItems()}
      </div>
    </>
  )
}
Example #5
Source File: index.tsx    From excalideck with MIT License 5 votes vote down vote up
export default function SlideMiniatureImage({ deck, slide }: Props) {
    const containerRef = useRef<HTMLDivElement>(null);

    const [debouncedDeck] = useDebounce(
        deck,
        SLIDE_MINIATURE_IMAGE_RENDER_DEBOUNCE
    );
    const [debouncedSlide] = useDebounce(
        slide,
        SLIDE_MINIATURE_IMAGE_RENDER_DEBOUNCE
    );

    useEffect(() => {
        // When no canvas has been rendered yet (i.e. it's the first render),
        // and there is a cached canvas, render it
        if (!isCanvasRendered(containerRef)) {
            const cachedSlideCanvas = slideCanvasCache.get(debouncedSlide.id);
            if (cachedSlideCanvas) {
                renderCanvas(containerRef, cachedSlideCanvas);
            }
        }

        // Update the canvas cache and re-render the canvas. This operation is
        // deferred to avoid blocking other more important work, like switching
        // view
        setTimeout(() => {
            const cachedSlideCanvas = slideCanvasCache.get(debouncedSlide.id);
            const updatedSlideCanvas = canvasSlideRenderer.renderSlide(
                debouncedDeck,
                debouncedSlide.id
            );
            if (updatedSlideCanvas !== cachedSlideCanvas) {
                renderCanvas(containerRef, updatedSlideCanvas);
                slideCanvasCache.set(debouncedSlide.id, updatedSlideCanvas);
            }
        }, 0);
    }, [debouncedDeck, debouncedSlide]);

    const slideAspectRatio =
        deck.printableArea.width / deck.printableArea.height;
    const miniatureAspectRatio = 3 / 2;
    return (
        <div
            ref={containerRef}
            className={clsx(
                "SlideMiniatureImage",
                slideAspectRatio < miniatureAspectRatio
                    ? "HeightCappedSlideMiniatureImage"
                    : "WidthCappedSlideMiniatureImage"
            )}
        />
    );
}
Example #6
Source File: ConnectTableSelect.tsx    From firetable with Apache License 2.0 4 votes vote down vote up
export default function ConnectTableSelect({
  value = [],
  onChange,
  column,
  row,
  config,
  disabled,
  className,
  TextFieldProps = {},
  onClose,
  loadBeforeOpen,
}: IConnectTableSelectProps) {
  // Store a local copy of the value so the dropdown doesn’t automatically close
  // when the user selects a new item and we allow for multiple selections
  const [localValue, setLocalValue] = useState(
    Array.isArray(value) ? value : []
  );
  const filters = config.filters
    ? config.filters.replace(/\{\{(.*?)\}\}/g, replacer(row))
    : "";
  const algoliaIndex = config.index;

  const [algoliaSearchKeys, setAlgoliaSearchKeys] = useAlgoliaSearchKeys<any>(
    {}
  );
  const [algoliaState, requestDispatch, , setAlgoliaConfig] = useAlgolia(
    process.env.REACT_APP_ALGOLIA_APP_ID!,
    process.env.REACT_APP_ALGOLIA_SEARCH_API_KEY ?? "",
    // Don’t choose the index until the user opens the dropdown if !loadBeforeOpen
    loadBeforeOpen ? algoliaIndex : "",
    { filters }
  );

  const setAlgoliaSearchKey = async (algoliaIndex: string) => {
    const requestedAt = Date.now() / 1000;
    if (
      algoliaSearchKeys &&
      (algoliaSearchKeys?.[algoliaIndex] as any)?.key &&
      requestedAt <
        (algoliaSearchKeys?.[algoliaIndex] as any).requestedAt + 3600
    ) {
      //'use existing key'
      setAlgoliaConfig({
        indexName: algoliaIndex,
        searchKey: (algoliaSearchKeys?.[algoliaIndex] as any).key,
      });
    } else {
      //'get new key'
      const resp = await getAlgoliaSearchKey(algoliaIndex);
      const key = resp.data.data;
      if (key) {
        const newKey = {
          key,
          requestedAt,
        };
        setAlgoliaSearchKeys(
          algoliaSearchKeys
            ? { ...algoliaSearchKeys, [algoliaIndex]: newKey }
            : { [algoliaIndex]: newKey }
        );
        setAlgoliaConfig({ indexName: algoliaIndex, searchKey: key });
      }
    }
  };

  useEffect(() => {
    if (!process.env.REACT_APP_ALGOLIA_SEARCH_API_KEY)
      setAlgoliaSearchKey(algoliaIndex);
  }, [algoliaIndex]);

  const options = algoliaState.hits.map((hit) => ({
    label: config.primaryKeys?.map((key: string) => hit[key]).join(" "),
    value: hit.objectID,
  }));

  // Pass a list of objectIDs to MultiSelect
  const sanitisedValue = localValue.map(
    (item) => item.docPath.split("/")[item.docPath.split("/").length - 1]
  );

  const handleChange = (_newValue) => {
    // Ensure we return an array
    const newValue = Array.isArray(_newValue)
      ? _newValue
      : _newValue !== null
      ? [_newValue]
      : [];

    // Calculate new value
    const newLocalValue = newValue.map((objectID) => {
      // If this objectID is already in the previous value, use that previous
      // value’s snapshot (in case it points to an object not in the current
      // Algolia query)
      const existingMatch = _find(localValue, {
        docPath: `${algoliaIndex}/${objectID}`,
      });
      if (existingMatch) return existingMatch;

      // If this is a completely new selection, grab the snapshot from the
      // current Algolia query
      const match = _find(algoliaState.hits, { objectID });
      const { _highlightResult, ...snapshot } = match;

      // Use snapshotFields to limit snapshots
      let partialSnapshot = snapshot;
      if (
        Array.isArray(config.snapshotFields) &&
        config.snapshotFields.length > 0
      )
        partialSnapshot = _pick(snapshot, config.snapshotFields);

      return {
        snapshot: partialSnapshot,
        docPath: `${algoliaIndex}/${snapshot.objectID}`,
      };
    });

    // If !multiple, we MUST change the value (bypassing localValue),
    // otherwise `setLocalValue` won’t be called in time for the new
    // `localValue` to be read by `handleSave`
    if (config.multiple === false) onChange(newLocalValue);
    // Otherwise, `setLocalValue` until user closes dropdown
    else setLocalValue(newLocalValue);
  };

  // Save when user closes dropdown
  const handleSave = () => {
    if (config.multiple !== false) onChange(localValue);
    if (onClose) onClose();
  };
  // Change MultiSelect input field to search Algolia directly
  const [search, setSearch] = useState("");
  const [debouncedSearch] = useDebounce(search, 1000);
  useEffect(() => {
    requestDispatch({ query: debouncedSearch });
  }, [debouncedSearch]);

  return (
    <MultiSelect
      value={config.multiple === false ? sanitisedValue[0] : sanitisedValue}
      onChange={handleChange}
      onOpen={() => {
        setAlgoliaConfig({
          indexName: algoliaIndex,
        });
        requestDispatch({ filters });
      }}
      onClose={handleSave}
      options={options}
      TextFieldProps={{
        className,
        hiddenLabel: true,
        ...TextFieldProps,
      }}
      label={column?.name}
      labelPlural={config.searchLabel}
      multiple={(config.multiple ?? true) as any}
      {...({
        AutocompleteProps: {
          loading: algoliaState.loading,
          loadingText: <Loading />,
          inputValue: search,
          onInputChange: (_, value, reason) => {
            if (reason === "input") setSearch(value);
          },
          filterOptions: () => options,
        },
      } as any)}
      countText={`${localValue.length} of ${
        algoliaState.response?.nbHits ?? "?"
      }`}
      disabled={disabled}
    />
  );
}
Example #7
Source File: ImageSettings.tsx    From freedeck-configurator with GNU General Public License v3.0 4 votes vote down vote up
ImageSettings: React.FC<{
  setImageSettings: (settings: IDisplay["imageSettings"]) => void;
  setTextSettings: (settings: IDisplay["textSettings"]) => void;
  setTextWithIconSettings: (settings: IDisplay["textWithIconSettings"]) => void;
  textOnly: boolean;
  imageSettings: IDisplay["imageSettings"];
  textWithIconSettings: IDisplay["textWithIconSettings"];
  textSettings: IDisplay["textSettings"];
}> = ({
  setImageSettings,
  setTextSettings,
  setTextWithIconSettings,
  textOnly,
  imageSettings,
  textWithIconSettings,
  textSettings,
}) => {
  const [localText, setLocalText] = useState<string>(textSettings.text);
  const [localWhite, setLocalWhite] = useState<number>(
    imageSettings.whiteThreshold
  );
  const [localBlack, setLocalBlack] = useState<number>(
    imageSettings.blackThreshold
  );
  const [localBrightness, setLocalBrightness] = useState<number>(
    imageSettings.brightness
  );
  const [localContrast, setLocalContrast] = useState<number>(
    imageSettings.contrast
  );
  const [localIconWidth, setLocalIconWidth] = useState<number>(
    textWithIconSettings.iconWidthMultiplier
  );
  const [debouncedText] = useDebounce(localText, 33, {
    maxWait: 33,
    leading: true,
  });
  const [debouncedWhite] = useDebounce(localWhite, 33, {
    maxWait: 33,
    leading: true,
  });
  const [debouncedBlack] = useDebounce(localBlack, 33, {
    maxWait: 33,
    leading: true,
  });
  const [debouncedBrightness] = useDebounce(localBrightness, 33, {
    maxWait: 33,
    leading: true,
  });
  const [debouncedContrast] = useDebounce(localContrast, 33, {
    maxWait: 33,
    leading: true,
  });
  const [debouncedIconWidth] = useDebounce(localIconWidth, 33, {
    maxWait: 33,
    leading: true,
  });
  const setBlack = useCallback(
    (blackThreshold: number) => {
      setImageSettings({ ...imageSettings, blackThreshold });
    },
    [imageSettings, setImageSettings]
  );
  const setWhite = useCallback(
    (whiteThreshold: number) => {
      setImageSettings({ ...imageSettings, whiteThreshold });
    },
    [imageSettings, setImageSettings]
  );
  const setBrightness = useCallback(
    (brightness: number) => {
      setImageSettings({ ...imageSettings, brightness });
    },
    [imageSettings, setImageSettings]
  );
  const setContrast = useCallback(
    (contrast: number) => {
      setImageSettings({ ...imageSettings, contrast });
    },
    [imageSettings, setImageSettings]
  );
  const setInvert = useCallback(
    (invert: boolean) => {
      setImageSettings({ ...imageSettings, invert });
    },
    [imageSettings, setImageSettings]
  );
  const setDither = useCallback(
    (dither: any) => {
      setImageSettings({ ...imageSettings, dither });
    },
    [imageSettings, setImageSettings]
  );
  const setfontName = useCallback(
    (font: string) => {
      setTextSettings({ ...textSettings, font });
    },
    [textSettings, setTextSettings]
  );
  const setText = useCallback(
    (text: string) => {
      setTextSettings({ ...textSettings, text });
    },
    [setTextSettings, textSettings]
  );
  const setIconWidthMultiplier = useCallback(
    (value: number) => {
      setTextWithIconSettings({
        ...textWithIconSettings,
        iconWidthMultiplier: value,
      });
    },
    [setTextWithIconSettings, textWithIconSettings]
  );
  useEffect(() => {
    setLocalText(textSettings.text);
    setLocalWhite(imageSettings.whiteThreshold);
    setLocalBlack(imageSettings.blackThreshold);
    setLocalBrightness(imageSettings.brightness);
    setLocalContrast(imageSettings.contrast);
    setLocalIconWidth(textWithIconSettings.iconWidthMultiplier);
  }, [imageSettings, textSettings, textWithIconSettings]);
  useEffect(() => {
    setText(debouncedText);
    // eslint-disable-next-line
  }, [debouncedText]); // dont put setText there, we will have an endless loop if you do
  useEffect(() => {
    setWhite(debouncedWhite);
    // eslint-disable-next-line
  }, [debouncedWhite]);
  useEffect(() => {
    setBlack(debouncedBlack);
    // eslint-disable-next-line
  }, [debouncedBlack]);
  useEffect(() => {
    setBrightness(debouncedBrightness);
    // eslint-disable-next-line
  }, [debouncedBrightness]);
  useEffect(() => {
    setContrast(debouncedContrast);
    // eslint-disable-next-line
  }, [debouncedContrast]);
  useEffect(() => {
    setIconWidthMultiplier(debouncedIconWidth);
    // eslint-disable-next-line
  }, [debouncedIconWidth]);

  return (
    <Wrapper>
      <Column>
        <Disabler
          disable={textOnly}
          title="These options are disabled. Load an image by clicking on the black box or just enter some text"
        />
        <Title>Image Settings</Title>
        {!imageSettings.dither ? (
          <>
            <Row>
              <Label>White Threshold:</Label>
              <Value>{imageSettings.whiteThreshold}</Value>
            </Row>
            <Row>
              <StyledSlider
                min={0}
                max={128}
                step={1}
                value={localWhite}
                onChange={(event) =>
                  setLocalWhite(event.currentTarget.valueAsNumber)
                }
              />
            </Row>
            <Row>
              <Label>Black Threshold:</Label>
              <Value>{imageSettings.blackThreshold}</Value>
            </Row>
            <Row>
              <StyledSlider
                min={128}
                max={255}
                step={1}
                value={localBlack}
                onChange={(event) =>
                  setLocalBlack(event.currentTarget.valueAsNumber)
                }
              />
            </Row>
          </>
        ) : (
          <>
            <Row>
              <Label>Brightness:</Label>
              <Value>{imageSettings.brightness}</Value>
            </Row>
            <Row>
              <StyledSlider
                min={-1}
                max={1}
                step={0.02}
                value={localBrightness}
                onChange={(event) =>
                  setLocalBrightness(event.currentTarget.valueAsNumber)
                }
              />
            </Row>
            <Row>
              <Label>Contrast:</Label>
              <Value>{imageSettings.contrast}</Value>
            </Row>
            <Row>
              <StyledSlider
                min={-1}
                max={1}
                step={0.02}
                value={localContrast}
                onChange={(event) =>
                  setLocalContrast(event.currentTarget.valueAsNumber)
                }
              />
            </Row>
          </>
        )}

        <Row>
          <MicroToggle
            activated={imageSettings.invert}
            width="48%"
            onClick={() => setInvert(!imageSettings.invert)}
          >
            Invert
          </MicroToggle>

          <MicroToggle
            activated={imageSettings.dither}
            width="48%"
            onClick={() => setDither(!imageSettings.dither)}
          >
            Dither
          </MicroToggle>
        </Row>
        <Row>
          <Label>Icon width:</Label>
          <Value>{textWithIconSettings.iconWidthMultiplier.toFixed(2)}</Value>
        </Row>
        <Row>
          <StyledSlider
            disabled={!textSettings.text.length}
            min={0.1}
            max={0.9}
            step={0.01}
            value={localIconWidth}
            onChange={(event) =>
              setLocalIconWidth(event.currentTarget.valueAsNumber)
            }
          />
        </Row>
      </Column>

      <Column>
        <Title>Text</Title>
        <Row>
          <TextInput
            placeholder={"Enter text"}
            value={localText}
            onChange={(e) => setLocalText(e.currentTarget.value)}
          />
        </Row>
        <Row>
          <Label>Font:</Label>
          <StyledSelect
            defaultValue={textSettings.font}
            onChange={(e) => setfontName(e.currentTarget.value)}
          >
            <option value={fontSmaller}>smaller</option>
            <option value={fontSmall}>small</option>
            <option value={fontMedium}>medium</option>
            <option value={fontLarge}>large</option>
          </StyledSelect>
        </Row>
      </Column>
    </Wrapper>
  );
}
Example #8
Source File: AddBucket.tsx    From console with GNU Affero General Public License v3.0 4 votes vote down vote up
AddBucket = ({ classes }: IAddBucketProps) => {
  const dispatch = useDispatch();

  const bucketName = useSelector(
    (state: AppState) => state.buckets.addBucketName
  );
  const versioningEnabled = useSelector(
    (state: AppState) => state.buckets.addBucketVersioningEnabled
  );
  const lockingEnabled = useSelector(
    (state: AppState) => state.buckets.addBucketLockingEnabled
  );
  const quotaEnabled = useSelector(
    (state: AppState) => state.buckets.addBucketQuotaEnabled
  );
  const quotaType = useSelector(
    (state: AppState) => state.buckets.addBucketQuotaType
  );
  const quotaSize = useSelector(
    (state: AppState) => state.buckets.addBucketQuotaSize
  );
  const quotaUnit = useSelector(
    (state: AppState) => state.buckets.addBucketQuotaUnit
  );
  const retentionEnabled = useSelector(
    (state: AppState) => state.buckets.addBucketRetentionEnabled
  );
  const retentionMode = useSelector(
    (state: AppState) => state.buckets.addBucketRetentionMode
  );
  const retentionUnit = useSelector(
    (state: AppState) => state.buckets.addBucketRetentionUnit
  );
  const retentionValidity = useSelector(
    (state: AppState) => state.buckets.addBucketRetentionValidity
  );
  const distributedSetup = useSelector(selDistSet);
  const siteReplicationInfo = useSelector(selSiteRep);

  const [addLoading, setAddLoading] = useState<boolean>(false);
  const [sendEnabled, setSendEnabled] = useState<boolean>(false);
  const [lockingFieldDisabled, setLockingFieldDisabled] =
    useState<boolean>(false);

  const addRecord = (event: React.FormEvent) => {
    event.preventDefault();
    if (addLoading) {
      return;
    }
    setAddLoading(true);

    let request: MakeBucketRequest = {
      name: bucketName,
      versioning:
        distributedSetup && !siteReplicationInfo.enabled
          ? versioningEnabled
          : false,
      locking: distributedSetup ? lockingEnabled : false,
    };

    if (distributedSetup) {
      if (quotaEnabled) {
        const amount = getBytes(quotaSize, quotaUnit, true);
        request.quota = {
          enabled: true,
          quota_type: quotaType,
          amount: parseInt(amount),
        };
      }

      if (retentionEnabled) {
        request.retention = {
          mode: retentionMode,
          unit: retentionUnit,
          validity: retentionValidity,
        };
      }
    }

    api
      .invoke("POST", "/api/v1/buckets", request)
      .then((res) => {
        setAddLoading(false);
        const newBucketName = `${bucketName}`;
        resetForm();
        history.push(`/buckets/${newBucketName}/browse`);
      })
      .catch((err: ErrorResponseHandler) => {
        setAddLoading(false);
        dispatch(setErrorSnackMessage(err));
      });
  };

  const [value] = useDebounce(bucketName, 1000);

  useEffect(() => {
    dispatch(addBucketName(value));
  }, [value, dispatch]);

  const resetForm = () => {
    dispatch(addBucketName(""));
    dispatch(addBucketVersioning(false));
    dispatch(addBucketEnableObjectLocking(false));
    dispatch(addBucketQuota(false));
    dispatch(addBucketQuotaType("hard"));
    dispatch(addBucketQuotaSize("1"));
    dispatch(addBucketQuotaUnit("Ti"));
    dispatch(addBucketRetention(false));
    dispatch(addBucketRetentionMode("compliance"));
    dispatch(addBucketRetentionUnit("days"));
    dispatch(addBucketRetentionValidity(180));
  };

  useEffect(() => {
    let valid = false;

    if (bucketName.trim() !== "") {
      valid = true;
    }

    if (quotaEnabled && valid) {
      if (quotaSize.trim() === "" || parseInt(quotaSize) === 0) {
        valid = false;
      }
    }

    if (!versioningEnabled || !retentionEnabled) {
      dispatch(addBucketRetention(false));
      dispatch(addBucketRetentionMode("compliance"));
      dispatch(addBucketRetentionUnit("days"));
      dispatch(addBucketRetentionValidity(180));
    }

    if (retentionEnabled) {
      // if retention is enabled, then objec locking should be enabled as well
      dispatch(addBucketEnableObjectLocking(true));
      setLockingFieldDisabled(true);
    } else {
      setLockingFieldDisabled(false);
    }

    if (
      retentionEnabled &&
      (Number.isNaN(retentionValidity) || retentionValidity < 1)
    ) {
      valid = false;
    }

    setSendEnabled(valid);
  }, [
    bucketName,
    retentionEnabled,
    lockingEnabled,
    quotaType,
    quotaSize,
    quotaUnit,
    quotaEnabled,
    dispatch,
    retentionValidity,
    versioningEnabled,
  ]);

  return (
    <Fragment>
      <PageHeader label={<BackLink to={"/buckets"} label={"Buckets"} />} />
      <PageLayout>
        <FormLayout
          title={"Create Bucket"}
          icon={<BucketsIcon />}
          helpbox={
            <HelpBox
              iconComponent={<BucketsIcon />}
              title={"Buckets"}
              help={
                <Fragment>
                  MinIO uses buckets to organize objects. A bucket is similar to
                  a folder or directory in a filesystem, where each bucket can
                  hold an arbitrary number of objects.
                  <br />
                  <br />
                  <b>Versioning</b> allows to keep multiple versions of the same
                  object under the same key.
                  <br />
                  <br />
                  <b>Object Locking</b> prevents objects from being deleted.
                  Required to support retention and legal hold. Can only be
                  enabled at bucket creation.
                  <br />
                  <br />
                  <b>Quota</b> limits the amount of data in the bucket.
                  <br />
                  <br />
                  <b>Retention</b> imposes rules to prevent object deletion for
                  a period of time.
                </Fragment>
              }
            />
          }
        >
          <form
            noValidate
            autoComplete="off"
            onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
              addRecord(e);
            }}
          >
            <Grid container marginTop={1} spacing={2}>
              <Grid item xs={12}>
                <InputBoxWrapper
                  id="bucket-name"
                  name="bucket-name"
                  autoFocus={true}
                  onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
                    dispatch(addBucketName(event.target.value));
                  }}
                  label="Bucket Name"
                  value={bucketName}
                />
              </Grid>
              <Grid item xs={12}>
                <SectionTitle>Features</SectionTitle>
                {!distributedSetup && (
                  <Fragment>
                    <div className={classes.error}>
                      These features are unavailable in a single-disk setup.
                      <br />
                      Please deploy a server in{" "}
                      <a
                        href="https://docs.min.io/minio/baremetal/installation/deploy-minio-distributed.html?ref=con"
                        target="_blank"
                        rel="noreferrer"
                      >
                        Distributed Mode
                      </a>{" "}
                      to use these features.
                    </div>
                    <br />
                    <br />
                  </Fragment>
                )}
              </Grid>

              <Grid item xs={12}>
                {siteReplicationInfo.enabled && (
                  <Fragment>
                    <br />
                    <div className={classes.alertVersioning}>
                      <InfoIcon /> Versioning setting cannot be changed as
                      cluster replication is enabled for this site.
                    </div>
                    <br />
                  </Fragment>
                )}
                <FormSwitchWrapper
                  value="versioned"
                  id="versioned"
                  name="versioned"
                  checked={versioningEnabled}
                  onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
                    dispatch(addBucketVersioning(event.target.checked));
                  }}
                  label={"Versioning"}
                  disabled={
                    !distributedSetup ||
                    lockingEnabled ||
                    siteReplicationInfo.enabled
                  }
                />
              </Grid>
              <Grid item xs={12}>
                <FormSwitchWrapper
                  value="locking"
                  id="locking"
                  name="locking"
                  disabled={lockingFieldDisabled || !distributedSetup}
                  checked={lockingEnabled}
                  onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
                    dispatch(
                      addBucketEnableObjectLocking(event.target.checked)
                    );
                    if (event.target.checked && !siteReplicationInfo.enabled) {
                      dispatch(addBucketVersioning(true));
                    }
                  }}
                  label={"Object Locking"}
                />
              </Grid>

              <Grid item xs={12}>
                <FormSwitchWrapper
                  value="bucket_quota"
                  id="bucket_quota"
                  name="bucket_quota"
                  checked={quotaEnabled}
                  onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
                    dispatch(addBucketQuota(event.target.checked));
                  }}
                  label={"Quota"}
                  disabled={!distributedSetup}
                />
              </Grid>
              {quotaEnabled && distributedSetup && (
                <React.Fragment>
                  <Grid item xs={12}>
                    <InputBoxWrapper
                      type="number"
                      id="quota_size"
                      name="quota_size"
                      onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                        if (e.target.validity.valid) {
                          dispatch(addBucketQuotaSize(e.target.value));
                        }
                      }}
                      label="Capacity"
                      value={quotaSize}
                      required
                      min="1"
                      pattern={"[0-9]*"}
                      overlayObject={
                        <InputUnitMenu
                          id={"quota_unit"}
                          onUnitChange={(newValue) => {
                            dispatch(addBucketQuotaUnit(newValue));
                          }}
                          unitSelected={quotaUnit}
                          unitsList={k8sScalarUnitsExcluding(["Ki"])}
                          disabled={false}
                        />
                      }
                    />
                  </Grid>
                </React.Fragment>
              )}
              {versioningEnabled && distributedSetup && (
                <Grid item xs={12}>
                  <FormSwitchWrapper
                    value="bucket_retention"
                    id="bucket_retention"
                    name="bucket_retention"
                    checked={retentionEnabled}
                    onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
                      dispatch(addBucketRetention(event.target.checked));
                    }}
                    label={"Retention"}
                  />
                </Grid>
              )}
              {retentionEnabled && distributedSetup && (
                <React.Fragment>
                  <Grid item xs={12}>
                    <RadioGroupSelector
                      currentSelection={retentionMode}
                      id="retention_mode"
                      name="retention_mode"
                      label="Mode"
                      onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
                        dispatch(
                          addBucketRetentionMode(e.target.value as string)
                        );
                      }}
                      selectorOptions={[
                        { value: "compliance", label: "Compliance" },
                        { value: "governance", label: "Governance" },
                      ]}
                    />
                  </Grid>
                  <Grid item xs={12}>
                    <InputBoxWrapper
                      type="number"
                      id="retention_validity"
                      name="retention_validity"
                      onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                        dispatch(
                          addBucketRetentionValidity(e.target.valueAsNumber)
                        );
                      }}
                      label="Validity"
                      value={String(retentionValidity)}
                      required
                      overlayObject={
                        <InputUnitMenu
                          id={"retention_unit"}
                          onUnitChange={(newValue) => {
                            dispatch(addBucketRetentionUnit(newValue));
                          }}
                          unitSelected={retentionUnit}
                          unitsList={[
                            { value: "days", label: "Days" },
                            { value: "years", label: "Years" },
                          ]}
                          disabled={false}
                        />
                      }
                    />
                  </Grid>
                </React.Fragment>
              )}
            </Grid>
            <Grid item xs={12} className={classes.buttonContainer}>
              <Button
                type="button"
                variant={"outlined"}
                className={classes.clearButton}
                onClick={resetForm}
              >
                Clear
              </Button>
              <Button
                type="submit"
                variant="contained"
                color="primary"
                disabled={addLoading || !sendEnabled}
              >
                Create Bucket
              </Button>
            </Grid>
            {addLoading && (
              <Grid item xs={12}>
                <LinearProgress />
              </Grid>
            )}
          </form>
        </FormLayout>
      </PageLayout>
    </Fragment>
  );
}
Example #9
Source File: UserSearch.tsx    From knboard with MIT License 4 votes vote down vote up
UserSearch = ({ boardId, tagsValue, setTagsValue }: Props) => {
  const theme = useTheme();
  const [open, setOpen] = useState(false);
  const [inputValue, setInputValue] = useState("");
  const [loading, setLoading] = useState(false);
  const [options, setOptions] = useState<UserOption[]>([]);
  const [debouncedInput] = useDebounce(inputValue, 300, {
    equalityFn: (a, b) => a === b,
  });

  useEffect(() => {
    if (!open) {
      setOptions([]);
      setLoading(false);
    }
  }, [open]);

  useEffect(() => {
    if (inputValue) {
      setLoading(true);
    }
  }, [inputValue]);

  useEffect(() => {
    const source = api.CancelToken.source();

    const fetchData = async () => {
      try {
        const response = await api(
          `${API_SEARCH_USERS}?board=${boardId}&search=${inputValue}`,
          { cancelToken: source.token }
        );
        setLoading(false);
        setOptions(response.data);
      } catch (err) {
        if (!api.isCancel(err)) {
          console.error(err);
        }
      }
    };

    if (inputValue === "") {
      setLoading(false);
      setOptions([]);
    } else {
      fetchData();
    }

    return () => {
      source.cancel("unmount/debouncedInput changed");
    };
  }, [debouncedInput, tagsValue]);

  useEffect(() => {
    if (debouncedInput === inputValue) {
      setLoading(false);
    }
  }, [debouncedInput, inputValue]);

  const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setInputValue(event.target.value);
  };

  const handleTagsChange = (_event: React.ChangeEvent<{}>, newValues: any) => {
    setTagsValue(newValues);
    setOptions([]);
  };

  return (
    <Autocomplete
      multiple
      id="user-search"
      size="small"
      open={open}
      onOpen={() => setOpen(true)}
      onClose={() => setOpen(false)}
      getOptionSelected={(option, value) => option.username === value.username}
      getOptionLabel={(option) => option.username}
      filterSelectedOptions
      onChange={handleTagsChange}
      options={options}
      loading={loading}
      value={tagsValue}
      renderOption={(option) => <AvatarOption option={option} />}
      renderInput={(params) => (
        <TextField
          {...params}
          autoFocus
          label="Search username"
          variant="outlined"
          onChange={handleInputChange}
          InputProps={{
            ...params.InputProps,
            endAdornment: (
              <>
                {loading && <CircularProgress color="inherit" size={20} />}
                {params.InputProps.endAdornment}
              </>
            ),
          }}
        />
      )}
      renderTags={(value, getTagProps) =>
        value.map((option, index) => (
          <AvatarTag
            key={option.id}
            option={option}
            {...getTagProps({ index })}
          />
        ))
      }
      css={css`
        width: ${theme.breakpoints.down("xs") ? 200 : 300}px;
      `}
    />
  );
}