Code Monkey home page Code Monkey logo

fml's People

Contributors

evanbb avatar

Watchers

 avatar  avatar  avatar

fml's Issues

Create a way to reuse configs across implementations

Part of the motivation behind this library is to be able to declare the interface you want in implementation-agnostic terms. It's currently possible to declare a bunch of component registry types and subsequently declare a corresponding configuration. However, it's completely possible to have the types declared but nothing in the runtime registry, resulting in a runtime exception.

Ideally, there would be a way to declare the types and a corresponding configuration, but not be able to, say, use that configuration for anything meaningful (like passing to a component to render) without adding an implementation to the runtime registry. It gets more complicated, though ๐Ÿ˜ญ

To pass that configuration to, say, a React component, should we require all configurations have a corresponding React implementation? To what extend can we mix-and-match implementations? E.g., we can potentially proxy data and function calls between an Angular and React component, similarly between Svelte and Custom Elements, etc., but some would work more easily than others out of the box.

Keeping the configuration agnostic of implementation details affords a couple of interesting abilities:

  • Mix/match/swap implementations without touching configurations
  • Reuse configurations across projects built with different implementations (e.g., one in React, another in Swift, another in Java...)

Form submission callback signature

The React implementation's Form component's onSubmit callback should more closely reflect the native event handler signature. As a user, it was confusing to have a second argument for the event and have to explicitly call preventDefault - putting the event as the first arg will feel more ergonomic.

React: further separate display/rendering from state management

As it stands now, it isn't as easy as I'd like it to be to alter the display of form sections (e.g., label styling, fieldset legends, etc.). I'd like to distill the React implementation down as much as possible to make it really trivial to change how something renders while providing the state propagation as close to "for free" as possible.

One idea: Export more generic hooks that provide the appropriate state management dynamically (so consumers don't have to learn multiple hooks, they just get callbacks and already-rendered content).

Another idea: Have a separate registry for renderers that can be dynamically resolved. Every overridable component would have to have a compatible interface.

More ideas might come later.

Layouts

Create typing extensions for alternate layouts, including:

  • Wizards
  • Drawers
  • Tabs

React: revise hook return method names

It's a bit awkward to have names like handleBlur return from the hook bind to onBlur on the component - would make it more ergonomic to match the native event handler names more closely and enable spreading of props

React: ease resolution of FmlComponentProps

Currently, config extensions have to be specified in two locations:

// first in the config registry augmentation:
declare module '@fml/core' {
  export interface FmlFieldControlRegistry<TValue>
    extends Record<string, FmlFieldControlRegistration<unknown>> {
    customImpl: [string | undefined, { extra: { stuff: boolean } }];
  }
}

// then again where the component is implemented and used:
interface CustomImplProps extends FmlComponentProps<string | undefined> {
  extra: {
    stuff: boolean;
  }
}

registerControl('customImpl', CustomImpl);

function MuiText(props: CustomImplProps) {
  // ...
}

As a consumer, it would be more convenient to resolve the config extension for the corresponding, already-registered component - something like this:

declare module '@fml/core' {
  export interface FmlFieldControlRegistry<TValue>
    extends Record<string, FmlFieldControlRegistration<unknown>> {
    customImpl: [string | undefined, { extra: { stuff: boolean } }];
  }
}

registerComponent('customImpl', CustomImpl)

function CustomImpl(props: FmlComponentProps<'customImpl'>) {
  // ...
}

React: revise handling of defaultValue

As a consumer, I want to optionally specify different default values for different form fields, but conflicts can arise when rendering a form with a default value set to the value of an existing object. For example:

interface Thing {
  dataProperty: string
}

const { data } = useQuery('whatever', () => fetch('/api/things/123'))
// async returns something like { dataProperty: 'i came from the api' }
...
return (
  <Form
    config={{
      defaultValue={data}
      schema: {
        dataProperty: {
          defaultValue: '' // ?!?! should this not use 'i came from the api' as the defaultValue?
        }
      }
    }}
  />
)

Gets much more complicated with lists and models ๐Ÿ˜ฌ

Behaviors

Create typing extensions for adding behaviors to sections of forms, such as

  • Hide a particular field if another field's value is greater than X
    • Field selections need to be strongly typed - this could be challenging :|
    • Take some influence from the MongoDB query syntax (eq, in, gt/gte, lt/lte, etc.) for comparisons
  • Disable a field if another field's value is equal to X
  • others?

React: revise hook onChange signature

It's awkward to have a value and validity in this object and have to pass both back to the change handler - let's

  • Output the value and validity separately
  • Push the validity checking responsibility back into the change handler provided by the hook, only requiring the consumer to pass the value like this: onChange={e => hookReturn.onChange({ value })}

Revise config contract

Given the stability of the function signature for JSX elements, I'd like to explore consistently configuring everything as follows:

[tagName, properties, ...validChildren]

Conditionally enable submission

As a user, it is often convenient to have custom submission handling. Let's make the onSubmit prop for the Form in the React implementation optional and conditionally render the submit button.

React: ease extension of FmlComponentProps<Value>

Types aren't defined to easily reflect how FmlComponent implementation configs can be extended.

As it stands right now, the extra props are mixed into props.config, like this:

declare module '@fml/core' {
  export interface FmlFieldControlRegistry<TValue>
    extends Record<string, FmlFieldControlRegistration<unknown>> {
    customImpl: [string | undefined, { stuff: boolean }];
  }
}

function CustomImpl(props) {
  console.log(props) // { config: { stuff: false, ...rest } }
}

but the interface makes it seem like it should add extra props to... well, y'know, the PROPS, like this:

interface CustomProps extends FmlComponentProps<string | undefined> {
  stuff: boolean;
}

function CustomImpl(props: CustomProps) {
  const {
    config,
    stuff // this doesn't actually exist, but the types make it seem like it ought to ๐Ÿ˜ญ 
  } = props
}

Let's add another generic argument for extra props to FmlComponentProps:

interface FmlComponentProps<Value, ExtraConfig extends never = never> {
  config: FmlConfiguration<TValue> & ExtraConfig;
}

Core: support string templating for labels

As a user, it is nice to have the label contain additional, contextual information about the property I am editing, especially in lists. For example:

<ul>
  <li>
    <label for='label1'>
      Phone number:
    </label>
    <input id='label1' type='text' />
  </li>
  <li>
    <label for='label2'>
      Phone number:      
    </label>
    <input id='label2' type='text' />
  </li>
  <li>
    <label for='label3'>
      Phone number:
    </label>
    <input id='label3' type='text' />
  </li>
</ul>

It sometimes isn't very clear which phone number the user is editing (home, business, etc.), and this is especially true when the list contains nested objects.

Ideally the string templating could safely resolve any nested property in the bound data, so we could do things like:

{
  label: 'Phone number ({ type }):'
}

Prefer single curly brackets or some other syntax? Don't want to confuse it with native string interpolation syntax...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.