question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Proposal: Custom Widget

See original GitHub issue

Demand Overview

We would allow component developers to customize the form widgets for the properties. And we may support these features for the widget:

  • Registering Widgets
  • Widget Props
  • Using Other Widgets In The Custom Widget
  • Using One Widget To Configure Multiple Properties
  • Widget Options

Scheme Design

Registering Widgets

We should register the widgets like registering the components, traits, and so on. Thus we also define a spec for widgets to describe their information:

interface WidgetSpec {
  version: string;
  metadata: {
    name: string;
  }
}

Widget Props

The interface of props which would pass to widget component:

interface WidgetProps {
  // the property schema
  schema: Schema & EditorSchema;
  // the value of the property
  value: any;
  // runtime services
  readonly services: Service;
  // components in the editor
  readonly components: Component[];
  // the handler of value changed
  onChange: (value: any)=> void;
  // render the sub-property schema normally
  renderBySchema: ({schema: Schema, onChange: (value: any)=> void, value: any})=> ReactElement;
}

Using Other Widgets In The Custom Widget

If component developers want to use other widgets for sub-properties, they can define the widgets in the sub-properties spec options and use renderBySchema function in the custom widget component.

In The following, I will show you an example of implementing a custom widget for the Table component’s column property.

First of all, we should define the column property’s spec.

{
  "column": Type.Array(Type.Object({
    ...,
    // use the common widget by its type
    "key": Type.KeyOf(...),
    // use the another widget defined in spec options
    "handlers": Type.Object({}, { "widget": "EventHandler" }),
  }, {
    "widget": "TableColumn"
  }))
}

And then we should implement the widget component and call renderBySchema inside.

function TableColumn (props) {
  const { schema, value, onChange, renderBySchema } = props;
  
  const onValueChange = (key, value)=> {
    onChange({
      ...value,
      [key]: value
    })
  }
  
  return (
    <div>
      {/* implement custom widget here */ }
      {/* use others widgets for some properties */ }
      { renderBySchema({ 
          schema: schema.properties.key, 
          value: value.key,
          onChange: (v)=> onValueChange('key', value) 
      }) }
      { renderBySchema({
          schema: schema.properties.handlers,
          value: value.handlers,
          onChange: (v)=> onValueChange('handlers', value) 
      }) }
    </div>
  )
}

Using One Widget To Config Multiple Properties

In some situations, some properties should be configured by the same widget is better.

For example, some components would have the location properties such as left, right, top, bottom, which should be configured by a location widget.

image.png

There are two ways that come to my mind. The first one is using Type.Object to wrap the properties which should configure by one widget. And another way is adding a new field like widgetGroup into property spec options to define a virtual group for properties.

The spec examples of these two ways are following this:

Type.Object

{
  "location": Type.Object({
    "left": Type.String(),
    "right": Type.String(),
    "top": Type.String(),
    "bottom": Type.String(),
  }, {
    "widget": "Location"
  })
}

Group

{
  "left": Type.String({ "widgetGroup": "Location" }),
  "right": Type.String({ "widgetGroup": "Location" }),
  "top": Type.String({ "widgetGroup": "Location" }),
  "bottom": Type.String({ "widgetGroup": "Location" }), 
}

The Type.Object way is already implemented and it’s easy to use. Although it makes the component developers need to unwrap the properties to take their values in the components, I think it’s doesn’t matter.

The second way needs more additional editor implementation to support it, which would break the currently onChange logic.

Thus I prefer the first way, which is to use Type.Object to define the properties group.

Widget Options

The component developers may also want to pass some additional options to the widget component. We would provide the new field widgetOptions in properties spec options.

In the above location widget example, we may want to pass a keymap to the widget component for key transformed. For example, if we have paddingLeft property instead of left property, we should transform it.

So, we can define the map in the widgetOptions to tell the custom widget how to transform the keys.

{
  "location": Type.Object({
    "paddingLeft": Type.String(),
    "paddingRight": Type.String(),
    "paddingTop": Type.String(),
    "paddingBottom": Type.String(),
  }, {
    "widget": "Location",
    "wigetOptions": {
      "map": {
        "left": "paddingLeft",
        "right" "paddingRight",
        "top": "paddingTop",
        "bottom": "paddingBottom"
      }
    }
  })
}

And then we read the widgetOptions in the widget component and implement the transformed logic.

function Location (props) {
  const { schema, value, onChange } = props;
  const { widgetOptions = {} } = schema;
  const { map = {} } = widgetOptions;
  
  const onValueChange = (key, value)=> {
     onChange({
       ...value,
       [map[key] ?? key]: value
     });
  }
  
  return (
    <div>
      <input 
        value={value[map.left ?? 'left']} 
        onChange={(v)=> { onValueChange('left', v) }} 
      />
      ...
    </div>
  )
}

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:8 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
MrWindlikecommented, Mar 4, 2022

OK. I’m starting to implement it and I will do these jobs:

  • Adding new @sunmao-ui/editor-sdk package
  • Adding ChakraUI components into the kit object and exporting it
  • Changing all the form widgets depend on this proposal and moving them to the @sunmao-ui/editor-sdk package
0reactions
Yuyz0112commented, Mar 1, 2022

Sounds good. @tanbowensg @xzdry please confirm the latest design before @MrWindlike starts to implement it.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Knowledge: Designing Custom Proposal Templates
Follow the steps below to learn how to customize the template theme to highlight your brand, and add images, slideshows, and custom text...
Read more >
Building a Custom Widget - Email widget - ipywidgets
To create a custom widget, you need to define the widget both in the ... email value: it must contain an "@" character')...
Read more >
Free Online Proposal Maker - Make Custom Proposals | Visme
Visme's online DIY tool lets you design stunning proposals using customizable templates, a drag-and-drop editor and millions of free graphic assets.
Read more >
Proposal: template-based widget rendering - Google Groups
implement custom widgets: add some context data, define a custom template and you're done. This approach has a couple of advantages and drawbacks....
Read more >
Re: [platform-swt-dev] Nebula Subproject Proposal - Custom Widget ...
Hello SWT developers, I am currently working to propose a subproject of Nebula called the Custom Widget Toolkit: http://www.aspencloud.com/cwt As the name ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found