hsuanyi-chou / shadcn-ui-expansions

More components built on top of shadcn-ui.
https://shadcnui-expansions.typeart.cc/
MIT License
1.1k stars 51 forks source link

elm.focus is not a function #71

Closed LevaniVashadze closed 7 months ago

LevaniVashadze commented 7 months ago

Uncaught (in promise) TypeError: elm.focus is not a function focus index.esm.mjs:472 _focusInput index.esm.mjs:1979 iterateFieldsByAction index.esm.mjs:694 _focusError index.esm.mjs:2185 handleSubmit index.esm.mjs:2233 React 23 renderReactElement index.js:421 doRender index.js:579 render index.js:596 hydrate index.js:724 pageBootrap page-bootstrap.js:24

next-dev.js:25 promise callback* next-dev.js:23 NextJS 7 This error often occurred for me, ``` ( )} /> ( )} /> ``` This is how I used the component, everything worked but when submitting it the focus error would pop up. https://github.com/react-hook-form/react-hook-form/issues/9398 I am passing my states to the components because I need to set selected values I get from the backend. I would appreciate help in this regard. Thanks in advance
hsuanyi-chou commented 7 months ago

This is not the correct way to use react-hook-form.

you should not use useState when working with react-hook-form. You can custom your value at onChange. Your value should be in react-hook-form’s provider.

LevaniVashadze commented 7 months ago

Thanks for the quick response, I got it working now, I added the t variable to translate the labels to the component and now it works. Without the weird stuff I did. Although it would be nice for the component to store an array of values instead of storing the labels as well, that's why I tried to change it so that I can save arrays of IDs in the DB and then convert them to label | id objects client side.

LevaniVashadze commented 7 months ago

Also a simple defaultSelectedOptions prop would have been nice, with that I could have achieved the result

LevaniVashadze commented 7 months ago
<div className="flex flex-wrap gap-1">
           <ScrollArea
              className={cn("h-full w-full", selected.length < 1 && "hidden")}
            >
              <div className="flex h-full flex-row flex-nowrap gap-x-1 pb-2">
                {selected.map((option) => {
                  return (
                    <Badge
                      key={option.value}
                      className={cn(
                        "text-nowrap data-[disabled]:bg-muted-foreground data-[disabled]:text-muted data-[disabled]:hover:bg-muted-foreground",
                        "data-[fixed]:bg-muted-foreground data-[fixed]:text-muted data-[fixed]:hover:bg-muted-foreground",
                        badgeClassName,
                      )}
                      data-fixed={option.fixed}
                      data-disabled={disabled}
                    >
                      {option.label}
                      <button
                        className={cn(
                          "ml-1 rounded-full outline-none ring-offset-background focus:ring-2 focus:ring-ring focus:ring-offset-2",
                          (disabled || option.fixed) && "hidden",
                        )}
                        onKeyDown={(e) => {
                          if (e.key === "Enter") {
                            handleUnselect(option);
                          }
                        }}
                        onMouseDown={(e) => {
                          e.preventDefault();
                          e.stopPropagation();
                        }}
                        onClick={() => handleUnselect(option)}
                      >
                        <X className="h-3 w-3 text-muted-foreground hover:text-foreground" />
                      </button>
                    </Badge>
                  );
                })}
              </div>
              <ScrollBar
                orientation="horizontal"
                className="h-1.5 *:bg-white/40"
              />
            </ScrollArea>
            {/* Avoid having the "Search" Icon */}
            <CommandPrimitive.Input
              {...inputProps}
              ref={inputRef}
              value={inputValue}
              disabled={disabled}
              onValueChange={(value) => {
                setInputValue(value);
                inputProps?.onValueChange?.(value);
              }}
              onBlur={(event) => {
                setOpen(false);
                inputProps?.onBlur?.(event);
              }}
              onFocus={(event) => {
                setOpen(true);
                triggerSearchOnFocus && onSearch?.(debouncedSearchTerm);
                inputProps?.onFocus?.(event);
              }}
              placeholder={
                hidePlaceholderWhenSelected && selected.length !== 0
                  ? ""
                  : placeholder
              }
              className={cn(
                "ml-2 flex-1 bg-transparent outline-none placeholder:text-muted-foreground",
                inputProps?.className,
              )}
            />
          </div>

On an unrelated note, this might be useful to add, a horizontal scrollbar, needs polishing and adjusting but you get the point

image

hsuanyi-chou commented 7 months ago

Just giving the value. That would be your default selections. Or giving the defaultValues at react-hook-form.

hsuanyi-chou commented 7 months ago

The input will automatically expand when you pick more options over the size.

image

A scroll area is also a good idea.


As a developer, it's easy to add and control it by giving props. 💪

As a user, I prefer to see all options at one sight rather than scrolling the input box 😅.

LevaniVashadze commented 7 months ago

I did see that it will grow, but sometimes it grew too much so I either had to limit the amount of tags they can pick or make it scrollable. I also got 2 of these above each other and soon enough they would grow too big

LevaniVashadze commented 7 months ago

Also I settled for defaultvalues and now store the labels as translation strings and then translate them inside the component by the added "t" prop