A library of high-quality primitives that extend SolidJS reactivity.

Overview

Solid Primitives

Solid Primitives

pnpm turborepo combined-downloads

A project that strives to develop high-quality, community contributed Solid primitives. All utilities are well tested and continuously maintained. Every contribution to the repository is checked for quality and maintained to the highest degree of excellence. The ultimate goal is to extend Solid's primary and secondary primitives with a set of tertiary primitives.

While Solid Primitives is not officially maintained by the SolidJS Core Team, it is managed by members of the SolidJS core and ecosystem teams. This separation allows the core library to iterate independently while allowing Solid Primitives to remain in-sync with future plans.

Philosophy

The goal of Solid Primitives is to wrap client and server side functionality to provide a fully reactive API layer. Ultimately the more rooted our tertiary primitives are, the more they act as foundation within Solid's base ecosystem. With well built and re-used foundations, the smaller (aggregate tree-shaking benefits), more concise (readability) and stable (consistent and managed testing + maintenance) applications can be overall.

Primitives

Name Stage Primitives Size NPM

Inputs

active-element STAGE createActiveElement
createFocusSignal
SIZE VERSION
input-mask STAGE createInputMask SIZE VERSION
keyboard STAGE useKeyDownList
useCurrentlyHeldKey
useKeyDownSequence
createKeyHold
createShortcut
SIZE VERSION
mouse STAGE createMousePosition
createPositionToElement
SIZE VERSION
pointer STAGE createPointerListeners
createPerPointerListeners
createPointerPosition
createPointerList
SIZE VERSION
scroll STAGE createScrollPosition
useWindowScrollPosition
SIZE VERSION
selection STAGE createSelection SIZE VERSION

Display & Media

audio STAGE makeAudio
makeAudioPlayer
createAudio
SIZE VERSION
bounds STAGE createElementBounds SIZE VERSION
devices STAGE createDevices
createMicrophones
createSpeakers
createCameras
createAccelerometer
createGyroscope
SIZE VERSION
idle STAGE createIdleTimer SIZE VERSION
intersection-observer STAGE makeIntersectionObserver
createIntersectionObserver
createViewportObserver
createVisibilityObserver
SIZE VERSION
media STAGE makeMediaQueryListener
createMediaQuery
createBreakpoints
usePrefersDark
SIZE VERSION
page-visibility STAGE createPageVisibility SIZE VERSION
resize-observer STAGE createResizeObserver
createWindowSize
createElementSize
SIZE VERSION
styles STAGE createRemSize SIZE VERSION

Browser APIs

broadcast-channel STAGE makeBroadcastChannel
createBroadcastChannel
SIZE VERSION
clipboard STAGE makeClipboard
createClipboard
copyToClipboard
SIZE VERSION
event-listener STAGE createEventListener
createEventSignal
createEventListenerMap
WindowEventListener
DocumentEventListener
SIZE VERSION
event-props STAGE createEventProps SIZE VERSION
fullscreen STAGE createFullscreen SIZE VERSION
geolocation STAGE createGeolocation
createGeolocationWatcher
SIZE VERSION
mutation-observer STAGE createMutationObserver SIZE VERSION
permission STAGE createPermission SIZE VERSION
storage STAGE createStorage
createCookieStorage
createAsyncStorage
createStorageSignal
createLocalStorage
createSessionStorage
SIZE VERSION
timer STAGE makeTimer
createTimer
createTimeoutLoop
createPolled
createIntervalCounter
SIZE VERSION
upload STAGE createFileUploader
createDropzone
SIZE VERSION
workers STAGE createWorker
createWorkerPool
createSignaledWorker
SIZE VERSION

Network

connectivity STAGE createConnectivitySignal SIZE VERSION
fetch STAGE createFetch SIZE VERSION
graphql STAGE createGraphQLClient SIZE VERSION
stream STAGE createStream
createAmplitudeStream
createMediaPermissionRequest
createAmplitudeFromStream
createScreen
SIZE VERSION

Utilities

context STAGE createContextProvider SIZE VERSION
cursor STAGE createElementCursor
createBodyCursor
SIZE VERSION
date STAGE createDate
createDateNow
createTimeDifference
createTimeDifferenceFromNow
createTimeAgo
createCountdown
createCountdownFromNow
SIZE VERSION
event-bus STAGE createSimpleEmitter
createEmitter
createEventBus
createEventHub
createEventStack
SIZE VERSION
event-dispatcher STAGE createEventDispatcher SIZE VERSION
i18n STAGE createI18nContext
useI18n
SIZE VERSION
immutable STAGE List of functions SIZE VERSION
map STAGE ReactiveMap
ReactiveWeakMap
SIZE VERSION
pagination STAGE createPagination SIZE VERSION
platform STAGE List of variables SIZE VERSION
promise STAGE promiseTimeout
raceTimeout
until
SIZE VERSION
props STAGE combineProps
filterProps
SIZE VERSION
scheduled STAGE debounce
throttle
scheduleIdle
leading
SIZE VERSION
script-loader STAGE createScriptLoader SIZE VERSION
set STAGE ReactiveSet
ReactiveWeakSet
SIZE VERSION
share STAGE createSocialShare SIZE VERSION

Reactivity

destructure STAGE destructure SIZE VERSION
memo STAGE createLatest
createWritableMemo
createLazyMemo
createAsyncMemo
createDebouncedMemo
createDebouncedMemoOn
createThrottledMemo
createPureReaction
createMemoCache
createReducer
SIZE VERSION
rootless STAGE createSubRoot
createCallback
createDisposable
createSharedRoot
SIZE VERSION
signal-builders STAGE List of builders SIZE VERSION
trigger STAGE createTrigger
createTriggerCache
createWeakTriggerCache
SIZE VERSION

Control Flow

keyed STAGE keyArray
Key
Entries
SIZE VERSION
range STAGE repeat
mapRange
indexRange
Repeat
Range
IndexRange
SIZE VERSION
refs STAGE mergeRefs
elements
refs
mapRemoved
Children
Refs
Ref
unmount
SIZE VERSION

Animation

raf STAGE createRAF
targetFPS
SIZE VERSION
tween STAGE createTween SIZE VERSION

Contribution Process

Solid Primitives strives to provide idiomatic Solid principles but also allow room for innovation and experimentation. In a growing community many opinions and patterns merge together to produce a de facto standard. Managing opinions and expectations can be difficult. As a result, in November 2021 Solid Primitives implemented a ratification/approval tracking process roughly modelled on TC39 Proposal Stage Process. The following summarizes these stages briefly:

Stage Description
X Deprecated or rejected
0 Initial submission
1 Demonstrations and examples
2 General use (experimental)
3 Pre-shipping (final effort)
4 Accepted/shipped

Any primitive Stage 0-1 should be used with caution and with the understanding that the design or implementation may change. Beyond Stage 2 we make an effort to mitigate changes. If a primitive reaches Stage 2 it's likely to remain an official package with additional approvement until fully accepted and shipped.

Design Maxims

Other frameworks have large and extremely well established ecosystems. Notably React which has a vast array of component and hooks. The amount of choice within the ecosystem is great but often these tools are built as one-offs resulting in often un-tested logic or are designed with narrow needs. Over time the less concise these building blocks are the more they tend to repeat themselves. Our goal with Primitives is to bring the community together to contribute, evolve and utilize a powerful centralize primitive foundation.

All our primitives are meant to be consistent and sustain a level of quality. We guarantee that each is created with the utmost care. Our primitives are:

  1. Documented and follow a consistent style guide
  2. Be well tested
  3. Small, concise and practical as possible
  4. A single primitive for a single purpose
  5. No dependencies or as few as possible
  6. SSR safe entries (or short-circuits where needed) provided
  7. Wrap base level Browser APIs
  8. Should be progressively improved for future features
  9. Be focused on composition vs. isolation of logic
  10. Community voice and needs guide road map and planning
  11. Strong TypeScript support
  12. Support for both CJS and ESM
  13. Solid performance!

Basic and Compound Primitives

Each primitive is designed with composition in mind. A major rule in designing our primitives is deciding that the interface of primitives should be composable or segmented. For this reason every API is intricately studied and considered to be composed (stacked with features) or decomposed into smaller units. Designing our primitives in this manner allows for better tree-shaking and extendable complexity only as needed. You should only ship what you have to by picking from existing primitives as your foundational building blocks.

Much of the design decisions in naming are best described in the 7 Lessons to Outlive React talk by swyx. We strive to follow a similar design pattern promoted by the React core team.

make (non-reactive) vs create (reactive)

Solid uses the create prefix to define a primitive that provides reactive utility. Solid Primitives reinforces this pattern but in an effort to enhance composability we have also introduced the make prefix for identifying non-reactive foundation primitives. Having a non-reactive alternative means that the primitive does the bare essentials such as cleaning up events or interupting a process. ie. makeTimer will create and clean-up the scheduler, providing only a clear method. createTimer provides a properly reactive primitive that composes it.

Managing Primitive Complexity

Solid Primitives is mostly about supplying 80-90% of the common-use cases for the end-user. We prefer to be less prescriptive than other hook libraries such as VueUse and supply granular solutions as opposed to monolithic primitives. The remaining 10-20% of complex use cases are likely not to be covered with this library. This is on purpose to limit the potential of bloat and extended complexity. This project strives to provide foundations and not cumulative solutions. We expect the broader ecosystem will fill the remaining need as further composition to this projects effort. This allows for just the right amount of prescription and opinion.

NPM Release and Repository Structure

Solid Primitives is a large and growing project and the way we manage and release updates has been setup to suit the projects scope. The approach we've taken with organizing our packages and npm releases is more granular than other projects such as VueUse which ship all updates in a single release.

There are a number of benefits to this including small download sizes, reducing bloat and not shipping experimental/unnecessary primitives that users don't need or want locally. This does mean a bit more effort to install precisely what you need. As a team we've decided that this tradeoff is beneficial and valuable.

CLI Helpers

Available node scripts for managing packages and creating new ones:

  • pnpm run update-readme - This will update the list of primitives by inspecting individual packages.
  • pnpm run new-package name-of-your-package - A helper to setup a primitive template package.
  • pnpm run changeset - This will create a changeset file for updated packages.

Planned Primitives

Display & Media

  • createDragAndDrop

Device

  • createBattery

Browser

Network

  • createNotification
  • createPush
  • createConnectionObserver

Inputs

  • createGesture (in progress)
  • createCompositionObserver (CompositionEvent observer)
  • createForm
  • createInput
  • createTouch

Utilities

  • createWorker (in progress)
  • createQueue
  • createEffectWhen
Comments
  • WIP [Stage-1]: Composite Effect primitive proposal

    WIP [Stage-1]: Composite Effect primitive proposal

    Coming from vue I thought of bringing some reactive utilities from vueuse, specifically the utilities focused of watching changes of computations. Most of them are very simple (maybe with exception of until), they're just helpers built on top of createRoot, createEffect and on that aim for reducing the amount of code for some repetitive use cases.

    Their usages can found be here, and the API for solid would be pretty similar.

    This is not finished, I just wanted to get feedback on some of the considerations:

    • First is the question: does Solid even need these? Solid aims to be simple, not easy, preferring to have things explicit and maybe even overly verbose just to keep everything under control. Maybe because of that some of these helpers don't really make sense, and I just want them because I'm used to having them in vue. Or perhaps some other versions would be more suitable. I know I'll definitely use some of them, like a watch, debouncedWatch, throttledWatch, and until are pretty useful.

    • The naming may be odd, since it doesn't follow the "create___" rule, but I think not everything has to.

    • Another thing is that I used debounce and throttle functions from lodash. I know that solid-primitives has its own createDebounced & createThrottled counterparts, but I don't understand why not just use the library that everyone will probably have in their codebase anyway, even if they are not using them directly. It's the same idea as with primitives, using what's already built and tested is better than writing your own.

    opened by thetarnav 39
  • improve createViewportObserver

    improve createViewportObserver

    An attempt to improve createViewportObserver. If I understand the idea of this helper correctly — observing a dynamic number of elements, while easily handling their visibility changes via independent callbacks — then there were two odd things: the "setter" type, and passing initial elements as an argument when those elements wouldn't have their callbacks set.

    opened by thetarnav 20
  • Fixes #74 ([RAF] allow limiting fps when > 60)

    Fixes #74 ([RAF] allow limiting fps when > 60)

    Related to #74

    The RAF package https://github.com/davedbase/solid-primitives/tree/main/packages/raf

    Provides a way to run animations slower than 60 fps. When animations are faster or equal to 60fps then it just uses the screen refresh rate and doesn't limit the execution (which could be high above 60 [300 in my case]). See

    https://github.com/davedbase/solid-primitives/blob/43d9abed17cd61cda4238957db82eabafe0ea5e9/packages/raf/src/index.ts#L38-L42

    After studying this code, I came to the realization there's a reason for the above. It's hard to target a frame rate, and it's harder without consuming resources(this is very important, we will know below).

    The problem is the following:

    When you target a frame rate, you expect something to run after some ms. If your targetFrameRate is 60, then you expect the next run to be in around or above 16ms. This is a problem because if you get 15ms (values below 16sm are only possible because of a high refresh rate screen), then that won't satisfy you, and you would wait for the next frame, which will be: the delay already applied :15ms + your screen refresh rate. In other words, you didn't accept 15ms as the time to run something, so you should wait for the next frame, which means that you should wait as long as your screen refresh time is.

    To illustrate it, chrome_b1hHQWkBHM Most of the time this value is near zero(if the callback does little to not work at all), We should always keep in mind that the worse case of this value would be your target frame ms -1 + your frame rate. Which explains why do you see on my screenshot values near 300 / 1000 and around 19ms. Check it yourself looking at the console after pressing start/stop https://codesandbox.io/s/solid-create-raf-forked-h2bk62?file=/src/index.tsx

    This is not a problem you can solve, as you can't change the past(if you were late you late!), but you can change the future, for the sake of "targeting a frame rate" See

        elapsed = timeStamp - lastRun;
        if (elapsed + missedBy >= interval) {
          lastRun = timeStamp;
          missedBy = Math.max(elapsed - interval, 0)
          callback(timeStamp);
        }
    

    The missedBy accounts for how much time was wasted on the last render, and consider it on the next frame to recover from that to give the best of the experiences.

    Here is the tricky part, there are two kinds of users for a targetFps:

    • you can target a frame rate consistently as desired by consuming all or most of my CPU because that's your requirement (by counting how many frames you painted, etc)
    • you can target a frame rate to do exactly the opposite, to clam down the CPU (theres no reason to run something so many times if you consider that less is better)

    I am satisfying the latter here, and the former maybe can be satisfied by another package(which Im not even sure its worthy because they will most likely do it themselves)

    Un/Related considerations of this pull request:

    • The signature of requestAnimationFrame doesn't match what's provided by this, so I made them match. this is no longer providing a delta, it provides what requestAnimationFrame gives, and if users want the delta we can do it on a future update as a secondary argument
    • It changes the targetFps default value from 60 to Infinity, that way we can target values above 60
    • it does some rename from fps to targetFps, as fps doesn't really mirror the current fps, it's just a target. I figure we can tell "at how many fps we are running and pass it down" as we are already running a RAF non-stop.
    • I moved most of the functions calls to above the loop, because I do not like to have functions calls in that loop for no reason, I would like to be "reactive", calling a function non-stop is not what I consider being reactive.
    • Oh, I forgot to say that his is a breaking change, so requires a mayor version.

    Disclaimer, I have no idea how to run this TypeScript, so I only tested by copying pasting on codesandbox, so if you can take a test 🙏 would be appreciated. I would like to try to setup myself to deal with TS, but well.

    opened by titoBouzout 13
  • add new package: broadcast-channel

    add new package: broadcast-channel

    Add Package: broadcast-channel

    Proposing a primitive to use BroadcastChannel API

    Screen Shot 2022-11-28 at 1 06 41 AM

    How to use it

    makeBroadcastChannel

    const { postMessage } = makeBroadcastChannel("test_channel");
    
    postMessage({ id: 2, message: "hi" });
    
    // Another browsing context
    const { onMessage } = makeBroadcastChannel("test_channel");
    
    onMessage(({ data }) => {
      console.log(data); // { id: 2, message: "hi" }
    });
    

    You can use the same channel easily across different components in the same context

    const Component_1 = () => {
      const { postMessage } = makeBroadcastChannel("river");
    
      const onClick = () => {
        postMessage("hi");
      };
    
      return <button onClick={onClick}>Send Message</button>;
    };
    
    const Component_2 = () => {
      const { onMessage } = makeBroadcastChannel("river");
      const [message, setMessage] = createSignal("");
    
      onMessage(({ data }) => {
        setMessage(data);
      });
    
      return <div>{message()}</div>;
    };
    
    const App = () => {
      const { onMessage } = makeBroadcastChannel("river");
    
      onMessage(({ data }) => {
        console.log(data);
      });
    
      return (
        <>
          <Component_1 />
          <Component_2 />
        </>
      );
    };
    

    createBroadcastChannel

    Access the reactive message() signal that updates when postMessage is fired from other contexts

    const { postMessage } = createBroadcastChannel("test_channel");
    
    postMessage({ id: 2, message: "hi" });
    
    // Another browsing context
    const { message } = createBroadcastChannel("test_channel");
    
    createEffect(
      on(
        message,
        data => {
          console.log(data); // { id: 2, message: "hi" }
        },
        { defer: true }
      )
    );
    

    Stackblitz Demo

    https://stackblitz.com/edit/vitejs-vite-5xren3?file=src%2Fmain.tsx Copy url in stackblitz container and open additional tabs to properly test Screen Shot 2022-11-28 at 1 02 36 AM

    Or run Demo Locally

    Check out add-package-broadcast-channel branch and look at dev directory.

    cd ./packages/broadcast-channel
    pnpm dev
    
    opened by aquaductape 11
  • ui: initial setup + some primitives

    ui: initial setup + some primitives

    Setup:

    • Package setup (ts, jest, eslint).
    • Moved all works from @hope-ui/primitives.
    • Updated root README.md.

    Features:

    • Added visually-hidden primitive.
    • Added toggle primitive.
    • Added switch primitive.
    • Added checkbox and checkbox-group primitives.
    • Added label primitive.
    opened by fabien-ml 11
  • fetch: rewrite with composability in mind

    fetch: rewrite with composability in mind

    Fresh from the discussion here https://github.com/solidjs-community/solid-primitives/discussions/129, this is the first draft of a simplified, more composable fetch primitive.

    This is still WIP; the retry and cache modifier are not yet done, the internal documentation and the README are not yet finished.

    opened by atk 10
  • package proposition: Range

    package proposition: Range

    If we decided to do a API preview/discussion before the implementation, then here we go :)

    I'm proposing adding a new Control Flow package, that would be adding a nicer and more optimised way of repeating a JSX element n-times. For example when wanting to display:

    • some kind of count with individual elements – number of stars by rendering each one as a star icon? 😅
    • numbers from x to y
    • ?

    Optimisation opportunities: (compared to using <Index> for it)

    • no array of empty items need to be created
    • there is no array, so there are no array items, so no signals for those items need to be created and kept in memory
    • cleaner interface

    The mapRange primitive – powering <Range> component:

    Usage:

    const [length, setLength] = createSignal(10)
    const mapped = mapRange(length, index => {
       const [value, setValue] = createSignal(index);
       createEffect(() => {...})
       return value
    })
    

    Definition:

    function mapRange<T>(
      length: Accessor<number>,
      mapFn: (i: number) => T,
      options?: { fallback?: Accessor<T> }
    ): Accessor<T[]>;
    

    The <Range> component:

    Usage:

    <Range of={10}>
       <div></div>
    </Range>
    
    with a render prop:
    <Range of={10}>
       {n => <div>{n}</div>}
    </Range>
    
    with fallback:
    <Range of={0} fallback={<p>no items...</p>}>
       <div></div>
    </Range>
    

    Comparing to how it currently can be achieved:

    <Index each={new Array(10)}>
       {(_, n) => <div>{n}</div>}
    </Index>
    

    Definition:

    function Range<T>(props: {
      of: number;
      fallback?: T;
      children: (index: number) => T;
    }): Accessor<T[]>;
    

    More props?

    I think that a single of prop is enough for most usage cases, and any range can still be achieved:

    {/* from 5 to 15 */}
    <Range of={10}>
       {n => <div>{n + 5}</div>}
    </Range>
    

    But a few additional props could possibly be helpful:

    • from - start of the range (0 by default)
    • to - end of the range (an alternative to of as it also dictates the range size)
    • by - increment numbers by n (1 by default)

    The downside of adding those could be:

    • increased complexity of the component
    • wasteful re-rendering of all of the elements in some cases (e.g. changing the increment_by value)

    Thoughts? :)

    Primitive Proposal 
    opened by thetarnav 9
  • [RAF] Remove FPS limiting to consider other possible features

    [RAF] Remove FPS limiting to consider other possible features

    Off the top of my head, I could think that what almost any fancy requestAnimationFrame need is:

    • targetFPS, run at most N FPS
    • tell at how many FPS we're running, to provide feedback, maybe only for development, but nice to have
    • tell the delta value, how much time elapsed since the last run

    What most people need is none of the above, but that's not the point. Others would find these values to be passed down useful.

    I'm proposing to start over, because this is a fresh and breaking change, and having people to use it, will make them to then have to change the way it's used in the future, and nobody likes that. The proposal made in https://github.com/solidjs-community/solid-primitives/pull/78 considered the above, but what got merged somehow is not considering it.

    The targetFPS callback that got implemented was very clever, but how do we add additional features that won't get repeated around and won't require changing signatures.

    opened by titoBouzout 8
  • new primitives: createFileUploader, createDropzone

    new primitives: createFileUploader, createDropzone

    Thank you for an amazing repo! It's my first ever open source PR, so any kind of feedback is highly appreciated.

    Snippet:

    // createFileUploader
    <button onClick={() => {
        selectFiles(([{ source, name, size, file }]) => console.log({ source, name, size, file })});
    }}>
    
    // multiple files
    const {files, selectFiles} = createFileUploader({ multiple: true, accept: "image/*" });
    selectFiles(files => files.forEach(file => console.log(file)));
    
    // single file
    const {file, selectFile} = createFileUploader();
    selectFiles(([{ source, name, size, file }]) => console.log({ source, name, size, file }));
    
    // createDropzone
    <div ref={dropzoneRef} style={{ width: "100px", height: "100px", background: "red" }}>Dropzone</div>
    
    const { setRef: dropzoneRef1, files: droppedFiles1 } = createDropzone({
      onDrop: async files => {
        await doStuff(2);
        files.forEach(f => console.log(f));
      },
      onDragStart: files => console.log("drag start")
      onDragStart: files => files.forEach(f => console.log(f)),
      onDragOver: files => console.log("drag over")
    });
    
    // fileUploader directive
    const [files, setFiles] = createSignal<UploadFile[]>([]);
    
    <input type="file" multiple use:fileUploader={{
      userCallback: fs => fs.forEach(f => console.log(f)),
      setFiles
    }} />
    

    Demo: https://codesandbox.io/s/solid-primitives-upload-ry042x?file=/src/index.tsx Reference: https://github.com/Marvinified/use-file-upload

    TODO:

    • [x] Port the React hook
    • [x] Add/expose additional helpers
    • [x] Add async callback support
    • [x] Improve types
    • [x] Add createDropzone
    • [x] Add fileUploader directive
    Primitive Proposal 
    opened by frixaco 7
  • add `createEventDispatcher` primitive

    add `createEventDispatcher` primitive

    A primitive that is the equivalent of Svelte's createEventDispatcher for Solid, enabling components to dispatch custom events.

    Use

    createEventDispatcher would take the component props as argument, and return an event dispatcher function.

    const dispatch = createEventDispatcher(props)
    

    A component can emit events by creating an event dispatcher and calling it. The event dispatcher takes three arguments, the event name (mandatory), the optional payload, and optional dispatcher options.

    <button onClick={() => dispatch('answer', 42}>Give Answer</button>
    

    The parent component would listen to the event as it would listen to any other event on a DOM element, by passing a on + capitalized name of the event prop containing the event handler. The payload will be carried in CustomEvent.detail

    <ChildComponent onAnswer={evt => console.log(evt.detail)} /> // will log 42
    

    The third parameter is an object that one property, cancelable, which determines whether the created custom event is cancelable, and defaults to { cancelable: false }. To parallel DOM's dispatchEvent, dispatch will return false, if the custom event is cancelable and preventDefault() has been called, true in all the other cases (even if there is no event handler associated to the event).

    TypeScript

    The developers should have access to the same suggestions and error signalling as if they were directly accessing the event handlers from the props. dispatch should suggest all the available events, suggest the payload type once the event is chosen, and consider whether a payload is optional or not (and thus signal the error if a mandatory payload is not passed). Some exemples :

    interface Props {
      onStringEvent: (evt: CustomEvent<string>) => void,
      onNumberEvent: (evt: CustomEvent<number>) => void,
      onMandatoryPayload: (evt: CustomEvent<number>) => void,
      onOptionalPayload: (evt?: CustomEvent<string>) => void,
    }
    
    dispatch() // will suggest ("stringEvent", "numberEvent", "mandatoryPayload", "optionalPayload")
    dispatch('stringEvent', ) // will suggest "(eventName: "changeStep", payload: string, dispatcherOptions?: DispatcherOptions | undefined) => boolean"
    dispatch('numberEvent', 'forty-two') // will throw "Error: Argument of type 'string' is not assignable to parameter of type 'number'."
    dispatch('mandatoryPayload') // will throw: "Error: Expected 2-3 arguments, but got 1."
    dispatch('optionalPayload') // will not complain, but suggest "(eventName: "optionalPayload", payload?: number | undefined, ...
    

    I have created a draft npm package that does it, to test if it was possible, and here an mock app that uses it, to see how it works :

    Primitive Proposal 
    opened by AyloSrd 6
  • feat(graphql): add support for `DocumentNode` and `TypedDocumentNode`

    feat(graphql): add support for `DocumentNode` and `TypedDocumentNode`

    This PR adds support DocumentNode and TypedDocumentNode types as arguments for the GraphQL query. With this change, it's now possible to use graphql primitive with the graphql-code-generator, which improves the TypeScript story for medium and large sized projects.

    This PR adds dependencies on the DocumentNode type from graphql package, TypedDocumentNode from @graphql-typed-document-node/core package (this type is just a thin wrapper around DocumentNode from graphql) and the print function from the graphql package (which is the only way to turn DocumentNode and TypedDocumentNode types into a string suitable for an API request).

    This PR also adds an example usage of such type inside the dev directory (adds a pnpm gqlgen command for generating the TypedDocumentNode types and uses generated type in one the two queries that already existed).

    opened by teenjuna 6
  • feat: solid-primitives-parser

    feat: solid-primitives-parser

    A primitive to tokenize your solid-components to enable custom parsing/custom renderers.

    It exists out of two parts, tokenize() and childrenTokens():

    • const token = tokenize(callback, meta)
      • takes two parameters:
        • a callback-function that takes in props just like a regular Solid-component
        • an object that can contain any additional metadata
      • returns a function token
        • this function spoofs to be a JSX.Element so it can be used in JSX without type-errors
        • its props are Object.assigned() to this spoof-function, so they can be accessed from their parent
        • a symbol $TOKEN for validation
    • const tokens = childrenTokens(() => props.children)
      • takes as parameter:
        • its children in a callback, similar to children()-api
      • returns all valid tokens as a memo
        • they are filtered by checking for the $TOKEN-symbol

    I made a very simple example in the repo with a calculator, and played around with a threejs-renderer

    I originally started playing with these type of hacks for this cms-experiment and @thetarnav mentioned that they also used something similar for solid-aria so it would be sweet to have this standardized. I think I will try to make a simplified version of the cms since it is quite a different usecase then the threejs-parser (callback() does return JSX which is being rendered, and data is inject from the parser).

    The Object.assign()-tip came from someone in the typescript-discord, either liquid or foolswisdom. The remark they gave back then was that it's impossible to type it in a way to disallow these spoofed JSX-elements to be used outside the Parser, this can only be done in runtime.

    talking points:

    • Right now the token.callback does not take any parameters, but instead the props of the component are being passed into the callback. you can inject or manipulate data with Dynamic in the case of when you would use the callback to render, and I think you might even be able to set them directly on the token-function with another Object.assign, but haven't tested this. Another option possible would be to have the callback-function inside token to take besides the component-props also an object coming from the parent-parser: so something like tokenize((props: {...}, parserParams: {...}) => {...}).
    • maybe instead of having tokenize pass in the component-props when you call callback() it should be done explicitly callback(token.props, {... parserParams ...}) to demistify the inner-workings.
    • maybe instead of the props being inferred from the callback-function it could be done by a generic: tokenize<Props>(() => {...}, {}). that way the callback is completely decoupled from its props, which feels a bit less spoofy and then you could type callback independently from the component's props. I imagine that in most of the usecase you do want those props though, so it will be a lot of token.callback(token.props), but that's maybe better then doing it automagically.
    • mix and matching multiple different parsers could get buggy since they are checking for the same symbol.

    TODOS:

    • [ ] the whole type-story can use some assistance, I am still a ts-noob
    • [ ] tests
    • [ ] docs

    Also very open for API-suggestions. I think this can become a very powerful tool in the solid toolchain, if done correctly. Since there is some spoofing going on to trick typescript to accept the JSX, the API-choices feel extra important.

    opened by bigmistqke 8
  • updating value with createLocalStorage inside createEffect causes

    updating value with createLocalStorage inside createEffect causes "too much recursion" error

    I use createLocalStorage from @solid-primitives/storage and encounter too much recursion error in browser when I did the following:

    import { createLocalStorage } from "@solid-primitives/storage";
    
    const [localStore, setLocalStore] = createLocalStorage({ prefix: "test" });
    
    createEffect(() => {
      if (localStore["token"] === undefined || localStore["token"] === "") {
        setLocalStore("token", "");
      }
    });
    

    Although I admit that there isn't much reason to create an effect like that, this is quite a simplified example, and I sometimes need to reset the state of the local storage under a certain condition, and that condition along with localStore["token" === ""] can be combined with other conditions with && or ||.

    And I think it can sometimes be easy to overlook an effect like that?

    I didn't expect that this could cause infinite loop or "too much recursion" as createSignal and createStore from solid-js don't seem to have this problem.

    import { createSignal } from "solid-js";
    
    const [token, setToken] = createSignal('');
    
    createEffect(() => {
      if (token === "") {
        setToken("");
      }
    });
    
    import { createStore } from "solid-js/store";
    
    const [state, setState] = createStore({
      value: "",
    });
    
    createEffect(() => {
      if (state["value"] === "") {
        setState("value", "");
      }
    });
    

    This could probably be fixed in user's code by creating a guard like:

    createEffect(() => {
      if (someCustomCondition...) {
        if (!localStore["token"] === "") {
          setLocalStore("token", "");
        }
      }
    });
    

    But could this be something that is good to fix? Just to make it behaves the same as createStore or createSignal from solid-js, which doesn't causes infinite loop whe updating it inside an effect like that

    opened by jhhom 0
  • [keyboard] shortcuts trigger default actions second time

    [keyboard] shortcuts trigger default actions second time

    Hey,

    When I setup the following shortcut (on Mac):

    createShortcut(['Meta', 'P'], () => {
        console.log('Pressed Meta + P');
    });
    

    Pressing this combo works fine, however if I fully let go, and press it again the native browser "Print" dialog will popup. I close it, press the combo again and it works fine, press again and the dialog opens (and so on...).

    I've tried with requireReset on both true and false but the behaviour is the same.

    This behaviour is the same on "Meta + S" too.

    opened by Ehesp 2
  • Allow to define function in i18n dictionaries

    Allow to define function in i18n dictionaries

    I think that it would be useful to support functions within dictionaries, such as:

    const dict = {
    en: {
      capitalize: (text: string) => text[0].toLocaleUpperCase() + text.slice(1),
      close: 'close',
      },
    fr: {
      close: 'fermer',
      }
    };
    

    Using functions would allow to rely on the default language for languages which have close rules and still be able to define variants for languages with different rules and write t('capitalize')('close')

    Currently, t('capitalize') returns an empty string.

    opened by evlist 0
  • [bug] input-mask does not treat my regex as I would expect

    [bug] input-mask does not treat my regex as I would expect

    Here is my codesandbox where I tried some experiments.

    I just wanted to see if I can make the input accept any number between 0 and 255 and block the user's input whenever they're typing something that would result in an invalid value.

    As you can see in the codesandbox, I've tried 3 different ways and they all fail to behave as desired in their own unique ways.

    opened by Evertt 2
Releases(@solid-primitives/[email protected])
Owner
SolidJS Community
Community managed SolidJS projects.
SolidJS Community
Free Open Source High Quality Dashboard based on Bootstrap 4 & React 16

Airframe React High Quality Dashboard / Admin / Analytics template that works great on any smartphone, tablet or desktop. Available as Open Source as

null 3.8k Jan 5, 2023
A template for seamlessly creating a SolidJS library with TypeScript

solidts-parcel A template for seamlessly creating a SolidJS library with TypeScript Using this template Press the green button saying "Use this templa

Tiago Cavalcante Trindade 5 Jun 14, 2022
Vite-solid-electron - A simple Vite, SolidJS, Electron integration template

vite-solid-electron Overview Very simple Vite, SolidJS, Electron integration tem

Christian Hansen 25 Dec 18, 2022
Demo of a micro frontend application with mixed React and SolidJS frameworks using Astro.

Micro Frontends with Astro This is a demo of a SolidJS and React mixed micro-frontend application using Astro as the base MPA framework. You can view

Enoch 5 Sep 5, 2022
Create reusable components with Alpine JS reactivity 🦑

Apline JS Component Alpine JS plugin x-component allows you to create reusable components, sprinkled with Alpine JS reactive data ?? Example ?? Page W

Mark Mead 66 Jan 5, 2023
Solid.js reactivity patterns for classes, and class components.

Tools for class-based reactivity powered by Solid.js, and for using classes as Solid components

LUME 25 Dec 17, 2022
A Frontend boilerplate for High productivity

Stapp frontend React 16, Redux 4, Router 4, Webpack 4 Domain-Driven File Structuring React/Redux No CSS frameworks (minimal styles starter) HMR suppor

Max Kokorin 22 Mar 22, 2020
IngredientRecipeFinder - Web app built with react using Edamam API to find any recipe for an ingredient search with nutrition filters (high protein, low carb,etc)

Ingredient Recipe Finder Web app This web app built with the use of Edamam API allows you to find any type of recipe for a specific ingredient you are

null 1 Jan 4, 2022
Template to create reactjs component library which will help you to create your dream library.

reactjs-library-template Template to create reactjs component library which will help you to create your dream library. How to use Commands to setup e

Nishant Tomar 1 Dec 25, 2021
General Component(GC, from now) is a React component generation library which helps you prototype your service quickly.

General Component(GC, from now) is a React component generation library which helps you prototype your service quickly.

null 12 Dec 4, 2021
A library to help you prototype React apps faster

Substantiate A library to help you prototype React apps faster. It is a standard React reducer with four extra properties: Takes a query function. Thi

Mary Rose Cook 5 Apr 24, 2022
Creates a Module Federation applcation, API server, or library based on one of multiple different templates.

create-mf-app Creates a Module Federation applcation, API server, or library based on one of multiple different templates. npx create-mf-app These pr

Jack Herrington 392 Dec 30, 2022
Starter template for Vite with React (TypeScript). Supports Tailwind with CSS-Modules. Jest and @react/testing-library configured and ready to go. Also ESLint, Prettier, Husky, Commit-lint and Atomic Design for components.

Mocking up web app with Vital(speed) Live Demo Features ⚡️ React 17 ?? TypeScript, of course ?? Jest - unitary testing made easy ?? Tailwind with JIT

Josep Vidal 141 Dec 29, 2022
Next js 12 boilerplate with SCSS, Jest, React Testing Library, Prettier, ESLint, Plop JS and more 🚀

Next.js 12 Complete Boilerplate This is a readme from next.js 12 complete boilerplate. Table of Contents Just to make your dev experience more simple

Douglas Henrique 51 Dec 29, 2022
The UI library for web3

web3-ui In Development ??️ A library of UI components specifically crafted for web3 use cases. The Motive The RFC RFC: web3-ui (a web3-specific UI lib

Developer DAO 742 Dec 27, 2022
A collection of testing recipes for React Testing Library

React Testing Library Recipes This repository is a collection of common testing recipes for using React Testing Library. It was created as a companion

Rupert McKay 3 Jan 19, 2022
Chakra UI component library starter template

Chakra Library ✨ ?? Chakra UI component library starter template. Fast builds with Vite ⚡ Pre-configured Storybook ?? Create release at GitHub or GitL

tundera 6 May 23, 2022
warning library for react native

react-native-warnings Description Warning library for react native highly customizable. Install $ yarn add react-native-warnings Demo Examples import

José Eduardo Morini Castro 5 Feb 18, 2022
Library of rowing clubs' blades

React Rowing Blades React Rowing Blades is a library of SVG illustrations (react components) of rowing clubs' blades. Install npm install --save react

John Walley 2 Nov 2, 2022