-
-
Notifications
You must be signed in to change notification settings - Fork 531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow building ESM based components #5593
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5593 +/- ##
==========================================
- Coverage 81.54% 81.11% -0.43%
==========================================
Files 319 323 +4
Lines 46915 47509 +594
==========================================
+ Hits 38255 38538 +283
- Misses 8660 8971 +311 ☔ View full report in Codecov by Sentry. |
WTF! Where did data go? Are parameter values now on model? And What about state etc? And remember that after layout and delete life cycle hooks are still needed. |
? |
Ah, got you. I was trying to copy the API of anywidget which does not separate |
If it was just possible to merge data and model? |
Regarding: "Give users the ability to write Preact components". Remember the purpose is not to enable preact. The purpose is to make it easy for users to create react components in a simple way. Almost like copy pasting existing examples. And those examples very often use JSX or even typescript/ TSX. With Preact its complicated to replicate React examples. Skilled React developers can do this via clever configuration of package.json file. But with In #5550 and inspired by IpyReact you can see how React, JSX and typescript/ TSX more easily can be supported using Sucrase. I think we should follow this path. We should either document how to use React + Sucrase with ReactiveHTML + |
Please consider adding the requirements from #5550 to your todo list in the first post 😄 |
One open question is also if you can use code in |
It's a bad idea because a user may define properties that clash with properties that are automatically inherited.
The idea there is that we already use and export Preact so they can start using it at zero extra cost. This does not preclude actual React based workflows but those will come at the cost of loading React on top of everything else.
Will have to think about that. |
I think we have to be a little more careful with our language. We've heard complaints that we weren't always very welcoming. |
Thanks. It was meant as a joke. But not perceived that way. |
a736c69
to
31e4890
Compare
With the latest version you can now leverage Preact from within the ESM bundle: import pathlib
import param
import panel as pn
from panel.reactive import ReactiveHTML
class Todo(ReactiveHTML):
tasks = param.List()
_esm = pathlib.Path(__file__).parent / 'preact.js'
preact = Todo(width=800, height=600)
preact.servable() function App(props) {
const [newTask, setNewTask] = useState('');
const [tasks, setTasks] = useState(props.tasks);
const addTodo = () => {
const task = { name: newTask, done: false }
setTasks([...tasks, task]);
props.data.tasks = [...tasks, task]
setNewTask('');
}
const onInput = (event) => {
const { value } = event.target;
setNewTask(value)
}
const onKeyUp = (event) => {
const { key } = event;
if (key === 'Enter') {
addTodo();
}
}
return html`
<div class="task-container">
<div class="task-input">
<input
placeholder="Add new item..."
type="text"
class="task-new"
value=${newTask}
onInput=${onInput}
onKeyUp=${onKeyUp}
/>
<button class="task-add" onClick=${addTodo}>Add</button>
</div>
<ul class="task-list">
${tasks.map((task, index) => html`
<li key=${index} class="task-item" v-for="task in state.tasks">
<label class="task-item-container">
<div class="task-checkbox">
<input type="checkbox" class="opacity-0 absolute" value=${task.done} />
<svg class="task-checkbox-icon" width=20 height=20 viewBox="0 0 20 20">
<path d="M0 11l2-2 5 5L18 3l2 2L7 18z" />
</svg>
</div>
<div class="ml-2">${task.name}</div>
</label>
</li>
`)}
</ul>
</div>
`;
}
export function render({ data, model, el }) {
return html`<${App} tasks=${data.tasks} data=${data}/>`
} |
I've got sucrase working now so you can write React code without |
So the example above now works with: interface Task {
name: string;
done: boolean
};
function App() {
const [newTask, setNewTask] = useState('');
const [tasks, setTasks] = useState([] as Task[]);
const addTodo = () => {
setTasks([...tasks, { name: newTask, done: false }]);
setNewTask('');
}
const onInput = (event: Event) => {
const { value } = event.target as HTMLInputElement;
setNewTask(value)
}
const onKeyUp = (event: KeyboardEvent) => {
const { key } = event;
if (key === 'Enter') {
addTodo();
}
}
return (
<div class="task-container">
<div class="task-input">
<input
placeholder="Add new item..."
type="text"
class="task-new"
value={newTask}
onInput={onInput}
onKeyUp={onKeyUp}
/>
<button class="task-add" onClick={addTodo}>Add</button>
</div>
<ul class="task-list">
{tasks.map((task, index) => (
<li key={index} class="task-item" v-for="task in state.tasks">
<label class="task-item-container">
<div class="task-checkbox">
<input type="checkbox" class="opacity-0 absolute" value={task.done} />
<svg class="task-checkbox-icon" viewBox="0 0 20 20">
<path d="M0 11l2-2 5 5L18 3l2 2L7 18z" />
</svg>
</div>
<div class="ml-2">{task.name}</div>
</label>
</li>
))}
</ul>
</div>
);
}
export function render({ data, model, el }) {
const app = <App tasks={data.tasks}/>;
return app;
} |
Okay I think this example demonstrates the React API fairly well: Pythonimport pathlib
import param
import panel as pn
from panel.reactive import ReactiveHTML
class Example(ReactiveHTML):
color = param.Color()
text = param.String()
_esm = pathlib.Path(__file__).parent / 'react_demo.js'
example = Example(text='Hello World!')
pn.Row(pn.Param(example.param, parameters=['color', 'text']), example).servable() JSXfunction App(props) {
const [color, setColor] = props.state.color
const [text, setText ] = props.state.text
const style = {color: color}
return (
<div>
<h1 style={style}>{text}</h1>
<input
value={text}
onChange={e => setText(e.target.value)}
/>
</div>
);
}
export function render({ state }) {
return <App state={state}/>;
} The global namespace also contains a React: {
Component,
useCallback,
useContext,
useEffect,
useErrorBoundary,
useLayoutEffect,
useState,
useReducer,
createElement: h
} |
I think it's a pretty clean API now. Here's another demo: reactive_esm_2x.mp4This demonstrates:
That said, I'm now considering whether we should split |
... and also considering whether I don't think But would The one I like the most is |
I think building a |
Yes, ESMComponent seems like a good name for it. And also yes, I'm working on adding the necessary shims to get mui working. |
I would think twice about putting ESM in the name :-) Focus on what the component can do for the developer, not what technology its build on. Its |
The downside of carving it out as a separate component is complexity. Will you explain both |
The same is true if you overload |
Would it be an idea to give |
Yes, in principle. The nice thing about |
This is now in a state I'm happy with and I'd like to get this merged to iterate further. |
Path
and then dynamically watch the path for changes