npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mistertemp/libs-front-shared

v3.1.3

Published

Shared React components and hooks for MisterTemp fronts built on `@mistertemp/design-system` (React 18): dropdown filters (generic, account, work environment, profession, text, date), `react-hook-form` date and time inputs, a copy-to-clipboard button, and

Readme

@mistertemp/libs-front-shared

Shared React components and hooks for MisterTemp fronts built on @mistertemp/design-system (React 18): dropdown filters (generic, account, work environment, profession, text, date), react-hook-form date and time inputs, a copy-to-clipboard button, and a few hooks.

Installation

npm install @mistertemp/libs-front-shared

Peer dependencies:

| Package | Version | | --- | --- | | @mistertemp/design-system | >=14.0.0 | | @mistertemp/libs-work-environments | ^1.0.0 | | react / react-dom | ^18.3.1 | | react-hook-form | ^7.81.0 | | i18next | ^25.4.2 | | i18next-browser-languagedetector | >=8.2.1 | | i18next-http-backend | >=3.0.2 | | react-i18next | ^15.7.4 |

The components also import lodash, fuse.js and date-fns at runtime. They are not declared as dependencies or peer dependencies, so the consuming app must install them.

The package ships its TypeScript sources ("main": "index.ts"), SCSS modules and JSON locale files, not a build. The consuming app's bundler must handle .tsx, .module.scss (with sass) and JSON imports. Some files import private paths of @mistertemp/design-system (@mistertemp/design-system/src/...).

Prerequisites

  • i18next must be initialized with react-i18next (initReactI18next) before the components render. The lib's labels are bundled and added to the current language at runtime, under the scc-common-filters and date-picker namespaces (French, Italian, and Spanish for the filters).
  • CopyToClipboard shows its toast through the design system's ToastContext: wrap the app in the design system's toast provider to see it.

Exports

Filters

All filters are design-system Dropdowns with a DropdownSelectorTrigger. The trigger title is "<placeholder> : <selected values>" when something is selected, and the dropdown has a "remove filter" CTA.

| Component | Value type | Description | | --- | --- | --- | | Filter<T> | T[] (option IDs) | Generic single or multiple selection list | | AccountFilter | string[] | Filter with a search input driven by the caller (server-side search) | | EnvironmentFilter | ENVIRONMENTS_CODE[] | Multiple selection of work environments, with a local fuzzy search | | ProfessionFilter | string[] (IDs in), FilterDropdownOption[] (out) | Multiple selection of professions, with a debounced search driven by the caller | | TextFilter | string | Free text input | | DateFilter | MixedDatePickerSelection | Date picker: single date, multiple dates or range |

Form inputs

| Component | Description | | --- | --- | | ControlledInputDate | react-hook-form date input with a configurable string format and min/max rules | | ControlledInputTime | react-hook-form time input with a configurable separator |

Other

| Export | Kind | Description | | --- | --- | --- | | CopyToClipboard | Component | Button that copies a value and optionally shows a toast | | useDebouncedSearchInput | Hook | Debounced search value with a minimum length | | usePrevious | Hook | Previous value of a variable | | useBundledTranslation | Hook | Adds bundled translations to the i18next instance | | SearchableFilterProps, DebouncedSearchConfig | Types | Search configuration | | FilterProps, FilterDropdownOption, AccountFilterProps, EnvironmentFilterProps, ProfessionFilterParentProps, ProfessionFilterDropdownOption, TextFilterProps, DateFilterProps, ControlledInputDateProps, ControlledInputTimeProps, CopyToClipboardProps, UseBundedTranslationResponse | Types | Props and return types |

Filters

Filter

import { Filter, FilterDropdownOption } from '@mistertemp/libs-front-shared';

type Status = 'open' | 'closed';

const statusOptions: FilterDropdownOption<Status>[] = [
    { id: 'open', label: 'Open' },
    { id: 'closed', label: 'Closed' },
];

export const StatusFilter = ({ value, onChange }: { value: Status[]; onChange: (v: Status[]) => void }) => (
    <Filter<Status>
        name="status"
        placeholder="Status"
        mode="multiple"
        options={statusOptions}
        dropdownValue={value}
        onDropdownValueChanged={onChange}
    />
);

| Prop | Type | Default | Description | | --- | --- | --- | --- | | name | string | — | Filter name, also the key of the dropdown value | | options | FilterDropdownOption<T>[] | — | { id: T; label: string; disabled?: boolean } | | mode | 'single' \| 'multiple' | 'single' | Radio-like list or checkboxes | | dropdownValue | T[] | — | Selected option IDs | | onDropdownValueChanged | (value: T[]) => void | — | Called when the dropdown closes, only if the selection changed | | onDropdownCTAClick | () => void | — | Called by the "remove filter" CTA, after the internal selection is cleared | | placeholder | string | — | Trigger title | | listProps | DropdownStaticListProviderProps (without name, selectionMode, items, itemTemplate, size) | — | Passed to the list, for example search or withSelectedTags | | selectorProps | DropdownSelectorTriggerProps (without isSelected, title) | — | Passed to the trigger | | data-testid | string | `filters+${name}` | The trigger gets +trigger | | ...props | DropdownProps (without trigger, dropdownValue, onDropdownValueChanged, onOpenChange) | — | Passed to the Dropdown. size defaults to 'small' |

The initial selection is read from dropdownValue on mount. Afterwards, dropdownValue changes only remove unselected options from the internal selection.

AccountFilter

Same props as Filter, except:

| Prop | Type | Default | Description | | --- | --- | --- | --- | | name | string | 'account' | | | options | FilterDropdownOption[] | — | Search results to display | | onInputValueChanged | (value: string \| undefined) => void | — | Called on every keystroke in the search input | | isLoading | boolean | false | Loading state of the search |

The search input and selected tags are configured through listProps: passing your own listProps replaces them.

<AccountFilter
    placeholder="Customer"
    mode="multiple"
    options={accounts.map(({ id, name }) => ({ id, label: name }))}
    isLoading={isFetching}
    onInputValueChanged={(text) => setSearch(text ?? '')}
    dropdownValue={selectedAccountIds}
    onDropdownValueChanged={setSelectedAccountIds}
/>

EnvironmentFilter

Options come from getAllFlattenedEnvironments() of @mistertemp/libs-work-environments (id is the ENVIRONMENTS_CODE). Typing in the search input filters them with fuse.js.

| Prop | Type | Default | Description | | --- | --- | --- | --- | | name | string | 'environment' | | | mode | 'single' \| 'multiple' | 'multiple' | | | fuseOptions | IFuseOptions<FilterDropdownOption<ENVIRONMENTS_CODE>> | — | Merged over the defaults: case insensitive, sorted, ignoreLocation, key label, threshold: 0.2 | | listProps | see Filter | — | Deep-merged over the built-in search and selected tags config | | other Filter props | | | Except name and options. The placeholder defaults to the translated "Environment" |

import { EnvironmentFilter } from '@mistertemp/libs-front-shared';
import { ENVIRONMENTS_CODE } from '@mistertemp/libs-work-environments';

const [environments, setEnvironments] = useState<ENVIRONMENTS_CODE[]>([]);

<EnvironmentFilter dropdownValue={environments} onDropdownValueChanged={setEnvironments} />;

ProfessionFilter

The caller owns the search: the filter reports the debounced search text, and the caller passes back the matching options. Selected options are listed first.

| Prop | Type | Default | Description | | --- | --- | --- | --- | | options | FilterDropdownOption[] | — | Professions to display | | dropdownValue | string[] | — | Selected profession IDs | | onChangeSelectedOptions | (options: FilterDropdownOption[]) => void | — | Called when the dropdown closes with a changed selection, and with [] on "remove filter" | | searchConfig | DebouncedSearchConfig | — | Required. See useDebouncedSearchInput | | onChangeSearchText | (text: string) => void | — | Called with the debounced search value | | isLoading | boolean | — | Loading state of the search | | placeholder | string | translated "Qualification" | Trigger title | | onDropdownCTAClick | () => void | — | Called after onChangeSelectedOptions([]) | | data-testid | string | 'filters+profession' | The search input gets +search | | ...props | DropdownProps | — | Passed to the Dropdown |

import { ProfessionFilter } from '@mistertemp/libs-front-shared';

<ProfessionFilter
    options={professions}
    isLoading={isFetching}
    dropdownValue={selectedIds}
    searchConfig={{ debounceDelay: 300, inputThreshold: 2, forceEmptyValue: true }}
    onChangeSearchText={setProfessionSearch}
    onChangeSelectedOptions={(options) => setSelectedIds(options.map(({ id }) => id))}
/>;

TextFilter

| Prop | Type | Default | Description | | --- | --- | --- | --- | | name | string | — | | | placeholder | string | — | Required. Trigger title | | dropdownValue | string | — | Current value | | onDropdownValueChanged | (value: string \| undefined) => void | — | Called on submit (Enter or the submit icon) and when the dropdown closes, if the value changed | | size | 'small' \| 'medium' | 'small' | Dropdown, trigger and input size | | onOpenChange | (isOpen: boolean) => void | — | Called before the value is committed on close | | selectorProps | DropdownSelectorTriggerProps (without isSelected, title, data-testid, disabled, size) | — | | | inputProps | InputTextProps (without data-testid, value, size, type, onSubmit, onChange, inputType) | — | | | data-testid | string | `filters+${name}` | The trigger gets +trigger, the input +input |

<TextFilter name="reference" placeholder="Reference" dropdownValue={reference} onDropdownValueChanged={setReference} />

DateFilter

| Prop | Type | Default | Description | | --- | --- | --- | --- | | name | string | 'date' | | | dropdownValue | MixedDatePickerSelection (Date, Date[] or DateRange) | — | An empty object counts as no value | | onDropdownValueChanged | (value: MixedDatePickerSelection) => void | — | Called when the picker is submitted and when the dropdown closes | | modes | DatePickerProps['availableModes'] | — | Available modes. With at least one, the mode switch is shown | | format | string (date-fns) | 'dd/MM/yy' | Display format in the trigger. Multiple dates show the earliest date and (+n) | | locale | Locale (date-fns) | fr | Picker locale | | selectorProps | DropdownSelectorTriggerProps (without isSelected, title) | — | | | disabled | boolean | — | Disables the trigger | | data-testid | string | `filters+${name}` | The trigger gets +trigger, the picker +picker |

The mode is deduced from the value (array, range or date), otherwise the first of modes, otherwise single. Cancel restores dropdownValue. Other DropdownProps are accepted by the type but not passed to the Dropdown.

import { DateFilter } from '@mistertemp/libs-front-shared';

<DateFilter
    modes={['single', 'range']}
    dropdownValue={period}
    onDropdownValueChanged={setPeriod}
/>;

Form inputs

Both inputs take the react-hook-form Controller props (name, control, rules, defaultValue…) plus inputProps for the design-system Input (without inputType and value). inputProps.onChange is called after the form value is updated, with a synthetic { target: { name, type, value } } event. hasError is set when the field has an error.

ControlledInputDate

| Prop | Type | Default | Description | | --- | --- | --- | --- | | format | string (date-fns) | 'dd/MM/yyyy' | Format of the form value | | minErrorMessage | string | 'min exceeded' | Message of the min rule | | maxErrorMessage | string | 'max exceeded' | Message of the max rule | | inputProps.min / inputProps.max | | — | Added to the controller rules as min / max |

The form value is a string in format, or '' when the input is cleared. On a min or max error, the input hint shows the error message, unless inputProps.hint is set: inputProps are spread last and win.

import { ControlledInputDate, ControlledInputTime } from '@mistertemp/libs-front-shared';
import { useForm } from 'react-hook-form';

type MissionForm = { startDate: string; startTime: string };

export const MissionDates = () => {
    const { control } = useForm<MissionForm>({ defaultValues: { startDate: '', startTime: '' } });

    return (
        <>
            <ControlledInputDate<MissionForm, 'startDate'>
                name="startDate"
                control={control}
                format="yyyy-MM-dd"
                rules={{ required: true }}
                minErrorMessage="The date is in the past"
                inputProps={{ label: 'Start date', min: '2026-01-01' }}
            />
            <ControlledInputTime<MissionForm, 'startTime'>
                name="startTime"
                control={control}
                separator=":"
                inputProps={{ label: 'Start time' }}
            />
        </>
    );
};

ControlledInputTime

| Prop | Type | Default | Description | | --- | --- | --- | --- | | separator | string | ':' | Separator in the form value | | inputProps.timeSeparator | string | design-system DEFAULT_TIME_SEPARATOR | Separator displayed in the input |

CopyToClipboard

| Prop | Type | Default | Description | | --- | --- | --- | --- | | value | string \| null | — | Text to copy. Nothing happens when it is empty | | toast | string \| Omit<ToastProps, 'id'> | — | A string shows a success toast with that title. Requires ToastContext | | onCopy | (event: MouseEvent, value: string) => void | — | Called after the copy | | icon | SvgIcon | DuplicateSvg | Button icon | | className | string | — | | | data-testid | string | 'copyToClipboard' | |

<CopyToClipboard value={candidate.email} toast="Email copied" />

It uses navigator.clipboard.writeText, which requires a secure context (HTTPS or localhost).

Hooks

useDebouncedSearchInput(inputValue, config?)

Returns a debounced search string that only changes when the input is long enough.

| Option (DebouncedSearchConfig) | Type | Default | Description | | --- | --- | --- | --- | | debounceDelay | number | 100 | Debounce delay in ms | | inputThreshold | number | 2 | The value is returned only when its length is strictly greater than this | | forceEmptyValue | boolean | — | Returns '' when the input drops to the threshold or below |

const [text, setText] = useState('');
const search = useDebouncedSearchInput(text, { debounceDelay: 300, forceEmptyValue: true });

useEffect(() => {
    fetchProfessions(search);
}, [search]);

usePrevious(value)

Returns the previous distinct value (compared with !==), or undefined until the value changes once.

useBundledTranslation(namespace, translations)

Adds translations[lang] to the current i18next instance under namespace (lang is the first two letters of i18n.language) if the bundle is missing, and returns { t, isReady }. Until ready, t returns the key. It relies on react-i18next's useTranslation, so i18next must already be initialized.

import { useBundledTranslation } from '@mistertemp/libs-front-shared';
import fr from './locales/fr/my-feature.json';
import it from './locales/it/my-feature.json';

const { t, isReady } = useBundledTranslation('my-feature', { fr, it });

Versioning

Versions are managed by release-please from Conventional Commits. Do not bump package.json by hand.