@pyscript/core 0.4.54 → 0.4.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/{codemirror-uEsvwGeX.js → codemirror-BZEh__gm.js} +2 -2
  2. package/dist/{codemirror-uEsvwGeX.js.map → codemirror-BZEh__gm.js.map} +1 -1
  3. package/dist/{codemirror_commands-BjErX5JV.js → codemirror_commands-leH8shna.js} +2 -2
  4. package/dist/{codemirror_commands-BjErX5JV.js.map → codemirror_commands-leH8shna.js.map} +1 -1
  5. package/dist/{codemirror_lang-python-rjOo-iGF.js → codemirror_lang-python-DuOzopOD.js} +2 -2
  6. package/dist/{codemirror_lang-python-rjOo-iGF.js.map → codemirror_lang-python-DuOzopOD.js.map} +1 -1
  7. package/dist/{codemirror_language-oTnz5LmX.js → codemirror_language-Dakzaxks.js} +2 -2
  8. package/dist/{codemirror_language-oTnz5LmX.js.map → codemirror_language-Dakzaxks.js.map} +1 -1
  9. package/dist/codemirror_view-Bm5_2vT5.js +2 -0
  10. package/dist/codemirror_view-Bm5_2vT5.js.map +1 -0
  11. package/dist/core-DKrwnOQh.js +3 -0
  12. package/dist/core-DKrwnOQh.js.map +1 -0
  13. package/dist/core.js +1 -1
  14. package/dist/{deprecations-manager-BxzDUsLg.js → deprecations-manager-BYGF73-y.js} +2 -2
  15. package/dist/{deprecations-manager-BxzDUsLg.js.map → deprecations-manager-BYGF73-y.js.map} +1 -1
  16. package/dist/{error-BiBuvdCh.js → error-DgYfWJQg.js} +2 -2
  17. package/dist/{error-BiBuvdCh.js.map → error-DgYfWJQg.js.map} +1 -1
  18. package/dist/{index-DvH26nkT.js → index-DA5aljNV.js} +2 -2
  19. package/dist/{index-DvH26nkT.js.map → index-DA5aljNV.js.map} +1 -1
  20. package/dist/{mpy-C8arSQdw.js → mpy-DiqSc6w9.js} +2 -2
  21. package/dist/mpy-DiqSc6w9.js.map +1 -0
  22. package/dist/{py-CQbAin00.js → py-DEubHlb_.js} +2 -2
  23. package/dist/{py-CQbAin00.js.map → py-DEubHlb_.js.map} +1 -1
  24. package/dist/{py-editor-Cd7PRgND.js → py-editor-DHmqLMO5.js} +2 -2
  25. package/dist/{py-editor-Cd7PRgND.js.map → py-editor-DHmqLMO5.js.map} +1 -1
  26. package/dist/{py-terminal-DCvjFHnO.js → py-terminal-E6BBcU6N.js} +2 -2
  27. package/dist/{py-terminal-DCvjFHnO.js.map → py-terminal-E6BBcU6N.js.map} +1 -1
  28. package/dist/{zip-BxogYCy6.js → zip-CGWtiqjJ.js} +2 -2
  29. package/dist/zip-CGWtiqjJ.js.map +1 -0
  30. package/package.json +7 -7
  31. package/src/plugins/py-terminal/mpy.js +19 -7
  32. package/src/stdlib/pyscript/event_handling.py +13 -12
  33. package/src/stdlib/pyscript/web/__init__.py +18 -4
  34. package/src/stdlib/pyscript/web/elements.py +898 -723
  35. package/src/stdlib/pyscript.js +3 -14
  36. package/types/core.d.ts +1 -1
  37. package/types/stdlib/pyscript.d.ts +0 -11
  38. package/dist/codemirror_view-CH_aW-qU.js +0 -2
  39. package/dist/codemirror_view-CH_aW-qU.js.map +0 -1
  40. package/dist/core-DsWVYGyA.js +0 -3
  41. package/dist/core-DsWVYGyA.js.map +0 -1
  42. package/dist/mpy-C8arSQdw.js.map +0 -1
  43. package/dist/zip-BxogYCy6.js.map +0 -1
  44. package/src/stdlib/pyscript/web/dom.py +0 -21
  45. package/src/stdlib/pyscript/web/media.py +0 -95
  46. package/src/stdlib/pyweb/__init__.py +0 -2
  47. package/src/stdlib/pyweb/media.py +0 -95
  48. package/src/stdlib/pyweb/pydom.py +0 -569
  49. package/src/stdlib/pyweb/ui/__init__.py +0 -1
  50. package/src/stdlib/pyweb/ui/elements.py +0 -947
@@ -1,569 +0,0 @@
1
- import inspect
2
-
3
- try:
4
- from typing import Any
5
- except ImportError:
6
- Any = "Any"
7
-
8
- try:
9
- import warnings
10
- except ImportError:
11
- # TODO: For now it probably means we are in MicroPython. We should figure
12
- # out the "right" way to handle this. For now we just ignore the warning
13
- # and logging to console
14
- class warnings:
15
- @staticmethod
16
- def warn(*args, **kwargs):
17
- print("WARNING: ", *args, **kwargs)
18
-
19
-
20
- try:
21
- from functools import cached_property
22
- except ImportError:
23
- # TODO: same comment about micropython as above
24
- cached_property = property
25
-
26
- try:
27
- from pyodide.ffi import JsProxy
28
- except ImportError:
29
- # TODO: same comment about micropython as above
30
- def JsProxy(obj):
31
- return obj
32
-
33
-
34
- from pyscript import display, document, window
35
-
36
- alert = window.alert
37
-
38
-
39
- class JSProperty:
40
- """JS property descriptor that directly maps to the property with the same
41
- name in the underlying JS component."""
42
-
43
- def __init__(self, name: str, allow_nones: bool = False):
44
- self.name = name
45
- self.allow_nones = allow_nones
46
-
47
- def __get__(self, obj, objtype=None):
48
- return getattr(obj._js, self.name)
49
-
50
- def __set__(self, obj, value):
51
- if not self.allow_nones and value is None:
52
- return
53
- setattr(obj._js, self.name, value)
54
-
55
-
56
- class BaseElement:
57
- def __init__(self, js_element):
58
- self._js = js_element
59
- self._parent = None
60
- self.style = StyleProxy(self)
61
- self._proxies = {}
62
-
63
- def __eq__(self, obj):
64
- """Check if the element is the same as the other element by comparing
65
- the underlying JS element"""
66
- return isinstance(obj, BaseElement) and obj._js == self._js
67
-
68
- @property
69
- def parent(self):
70
- if self._parent:
71
- return self._parent
72
-
73
- if self._js.parentElement:
74
- self._parent = self.__class__(self._js.parentElement)
75
-
76
- return self._parent
77
-
78
- @property
79
- def __class(self):
80
- return self.__class__ if self.__class__ != PyDom else Element
81
-
82
- def create(self, type_, is_child=True, classes=None, html=None, label=None):
83
- js_el = document.createElement(type_)
84
- element = self.__class(js_el)
85
-
86
- if classes:
87
- for class_ in classes:
88
- element.add_class(class_)
89
-
90
- if html is not None:
91
- element.html = html
92
-
93
- if label is not None:
94
- element.label = label
95
-
96
- if is_child:
97
- self.append(element)
98
-
99
- return element
100
-
101
- def find(self, selector):
102
- """Return an ElementCollection representing all the child elements that
103
- match the specified selector.
104
-
105
- Args:
106
- selector (str): A string containing a selector expression
107
-
108
- Returns:
109
- ElementCollection: A collection of elements matching the selector
110
- """
111
- elements = self._js.querySelectorAll(selector)
112
- if not elements:
113
- return None
114
- return ElementCollection([Element(el) for el in elements])
115
-
116
-
117
- class Element(BaseElement):
118
- @property
119
- def children(self):
120
- return [self.__class__(el) for el in self._js.children]
121
-
122
- def append(self, child):
123
- # TODO: this is Pyodide specific for now!!!!!!
124
- # if we get passed a JSProxy Element directly we just map it to the
125
- # higher level Python element
126
- if inspect.isclass(JsProxy) and isinstance(child, JsProxy):
127
- return self.append(Element(child))
128
-
129
- elif isinstance(child, Element):
130
- self._js.appendChild(child._js)
131
-
132
- return child
133
-
134
- elif isinstance(child, ElementCollection):
135
- for el in child:
136
- self.append(el)
137
-
138
- # -------- Pythonic Interface to Element -------- #
139
- @property
140
- def html(self):
141
- return self._js.innerHTML
142
-
143
- @html.setter
144
- def html(self, value):
145
- self._js.innerHTML = value
146
-
147
- @property
148
- def text(self):
149
- return self._js.textContent
150
-
151
- @text.setter
152
- def text(self, value):
153
- self._js.textContent = value
154
-
155
- @property
156
- def content(self):
157
- # TODO: This breaks with with standard template elements. Define how to best
158
- # handle this specifica use case. Just not support for now?
159
- if self._js.tagName == "TEMPLATE":
160
- warnings.warn(
161
- "Content attribute not supported for template elements.", stacklevel=2
162
- )
163
- return None
164
- return self._js.innerHTML
165
-
166
- @content.setter
167
- def content(self, value):
168
- # TODO: (same comment as above)
169
- if self._js.tagName == "TEMPLATE":
170
- warnings.warn(
171
- "Content attribute not supported for template elements.", stacklevel=2
172
- )
173
- return
174
-
175
- display(value, target=self.id)
176
-
177
- @property
178
- def id(self):
179
- return self._js.id
180
-
181
- @id.setter
182
- def id(self, value):
183
- self._js.id = value
184
-
185
- @property
186
- def options(self):
187
- if "options" in self._proxies:
188
- return self._proxies["options"]
189
-
190
- if not self._js.tagName.lower() in {"select", "datalist", "optgroup"}:
191
- raise AttributeError(
192
- f"Element {self._js.tagName} has no options attribute."
193
- )
194
- self._proxies["options"] = OptionsProxy(self)
195
- return self._proxies["options"]
196
-
197
- @property
198
- def value(self):
199
- return self._js.value
200
-
201
- @value.setter
202
- def value(self, value):
203
- # in order to avoid confusion to the user, we don't allow setting the
204
- # value of elements that don't have a value attribute
205
- if not hasattr(self._js, "value"):
206
- raise AttributeError(
207
- f"Element {self._js.tagName} has no value attribute. If you want to "
208
- "force a value attribute, set it directly using the `_js.value = <value>` "
209
- "javascript API attribute instead."
210
- )
211
- self._js.value = value
212
-
213
- @property
214
- def selected(self):
215
- return self._js.selected
216
-
217
- @selected.setter
218
- def selected(self, value):
219
- # in order to avoid confusion to the user, we don't allow setting the
220
- # value of elements that don't have a value attribute
221
- if not hasattr(self._js, "selected"):
222
- raise AttributeError(
223
- f"Element {self._js.tagName} has no value attribute. If you want to "
224
- "force a value attribute, set it directly using the `_js.value = <value>` "
225
- "javascript API attribute instead."
226
- )
227
- self._js.selected = value
228
-
229
- def clone(self, new_id=None):
230
- clone = Element(self._js.cloneNode(True))
231
- clone.id = new_id
232
-
233
- return clone
234
-
235
- def remove_class(self, classname):
236
- classList = self._js.classList
237
- if isinstance(classname, list):
238
- classList.remove(*classname)
239
- else:
240
- classList.remove(classname)
241
- return self
242
-
243
- def add_class(self, classname):
244
- classList = self._js.classList
245
- if isinstance(classname, list):
246
- classList.add(*classname)
247
- else:
248
- self._js.classList.add(classname)
249
- return self
250
-
251
- @property
252
- def classes(self):
253
- classes = self._js.classList.values()
254
- return [x for x in classes]
255
-
256
- def show_me(self):
257
- self._js.scrollIntoView()
258
-
259
- def snap(
260
- self,
261
- to: BaseElement | str = None,
262
- width: int | None = None,
263
- height: int | None = None,
264
- ):
265
- """
266
- Captures a snapshot of a video element. (Only available for video elements)
267
-
268
- Inputs:
269
-
270
- * to: element where to save the snapshot of the video frame to
271
- * width: width of the image
272
- * height: height of the image
273
-
274
- Output:
275
- (Element) canvas element where the video frame snapshot was drawn into
276
- """
277
- if self._js.tagName != "VIDEO":
278
- raise AttributeError("Snap method is only available for video Elements")
279
-
280
- if to is None:
281
- canvas = self.create("canvas")
282
- if width is None:
283
- width = self._js.width
284
- if height is None:
285
- height = self._js.height
286
- canvas._js.width = width
287
- canvas._js.height = height
288
-
289
- elif isinstance(to, Element):
290
- if to._js.tagName != "CANVAS":
291
- raise TypeError("Element to snap to must a canvas.")
292
- canvas = to
293
- elif getattr(to, "tagName", "") == "CANVAS":
294
- canvas = Element(to)
295
- elif isinstance(to, str):
296
- canvas = pydom[to][0]
297
- if canvas._js.tagName != "CANVAS":
298
- raise TypeError("Element to snap to must a be canvas.")
299
-
300
- canvas.draw(self, width, height)
301
-
302
- return canvas
303
-
304
- def download(self, filename: str = "snapped.png") -> None:
305
- """Download the current element (only available for canvas elements) with the filename
306
- provided in input.
307
-
308
- Inputs:
309
- * filename (str): name of the file being downloaded
310
-
311
- Output:
312
- None
313
- """
314
- if self._js.tagName != "CANVAS":
315
- raise AttributeError(
316
- "The download method is only available for canvas Elements"
317
- )
318
-
319
- link = self.create("a")
320
- link._js.download = filename
321
- link._js.href = self._js.toDataURL()
322
- link._js.click()
323
-
324
- def draw(self, what, width, height):
325
- """Draw `what` on the current element (only available for canvas elements).
326
-
327
- Inputs:
328
-
329
- * what (canvas image source): An element to draw into the context. The specification permits any canvas
330
- image source, specifically, an HTMLImageElement, an SVGImageElement, an HTMLVideoElement,
331
- an HTMLCanvasElement, an ImageBitmap, an OffscreenCanvas, or a VideoFrame.
332
- """
333
- if self._js.tagName != "CANVAS":
334
- raise AttributeError(
335
- "The draw method is only available for canvas Elements"
336
- )
337
-
338
- if isinstance(what, Element):
339
- what = what._js
340
-
341
- # https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
342
- self._js.getContext("2d").drawImage(what, 0, 0, width, height)
343
-
344
-
345
- class OptionsProxy:
346
- """This class represents the options of a select element. It
347
- allows to access to add and remove options by using the `add` and `remove` methods.
348
- """
349
-
350
- def __init__(self, element: Element) -> None:
351
- self._element = element
352
- if self._element._js.tagName.lower() != "select":
353
- raise AttributeError(
354
- f"Element {self._element._js.tagName} has no options attribute."
355
- )
356
-
357
- def add(
358
- self,
359
- value: Any = None,
360
- html: str = None,
361
- text: str = None,
362
- before: Element | int = None,
363
- **kws,
364
- ) -> None:
365
- """Add a new option to the select element"""
366
- # create the option element and set the attributes
367
- option = document.createElement("option")
368
- if value is not None:
369
- kws["value"] = value
370
- if html is not None:
371
- option.innerHTML = html
372
- if text is not None:
373
- kws["text"] = text
374
-
375
- for key, value in kws.items():
376
- option.setAttribute(key, value)
377
-
378
- if before:
379
- if isinstance(before, Element):
380
- before = before._js
381
-
382
- self._element._js.add(option, before)
383
-
384
- def remove(self, item: int) -> None:
385
- """Remove the option at the specified index"""
386
- self._element._js.remove(item)
387
-
388
- def clear(self) -> None:
389
- """Remove all the options"""
390
- for i in range(len(self)):
391
- self.remove(0)
392
-
393
- @property
394
- def options(self):
395
- """Return the list of options"""
396
- return [Element(opt) for opt in self._element._js.options]
397
-
398
- @property
399
- def selected(self):
400
- """Return the selected option"""
401
- return self.options[self._element._js.selectedIndex]
402
-
403
- def __iter__(self):
404
- yield from self.options
405
-
406
- def __len__(self):
407
- return len(self.options)
408
-
409
- def __repr__(self):
410
- return f"{self.__class__.__name__} (length: {len(self)}) {self.options}"
411
-
412
- def __getitem__(self, key):
413
- return self.options[key]
414
-
415
-
416
- class StyleProxy: # (dict):
417
- def __init__(self, element: Element) -> None:
418
- self._element = element
419
-
420
- @cached_property
421
- def _style(self):
422
- return self._element._js.style
423
-
424
- def __getitem__(self, key):
425
- return self._style.getPropertyValue(key)
426
-
427
- def __setitem__(self, key, value):
428
- self._style.setProperty(key, value)
429
-
430
- def remove(self, key):
431
- self._style.removeProperty(key)
432
-
433
- def set(self, **kws):
434
- for k, v in kws.items():
435
- self._element._js.style.setProperty(k, v)
436
-
437
- # CSS Properties
438
- # Reference: https://github.com/microsoft/TypeScript/blob/main/src/lib/dom.generated.d.ts#L3799C1-L5005C2
439
- # Following prperties automatically generated from the above reference using
440
- # tools/codegen_css_proxy.py
441
- @property
442
- def visible(self):
443
- return self._element._js.style.visibility
444
-
445
- @visible.setter
446
- def visible(self, value):
447
- self._element._js.style.visibility = value
448
-
449
-
450
- class StyleCollection:
451
- def __init__(self, collection: "ElementCollection") -> None:
452
- self._collection = collection
453
-
454
- def __get__(self, obj, objtype=None):
455
- return obj._get_attribute("style")
456
-
457
- def __getitem__(self, key):
458
- return self._collection._get_attribute("style")[key]
459
-
460
- def __setitem__(self, key, value):
461
- for element in self._collection._elements:
462
- element.style[key] = value
463
-
464
- def remove(self, key):
465
- for element in self._collection._elements:
466
- element.style.remove(key)
467
-
468
-
469
- class ElementCollection:
470
- def __init__(self, elements: [Element]) -> None:
471
- self._elements = elements
472
- self.style = StyleCollection(self)
473
-
474
- def __getitem__(self, key):
475
- # If it's an integer we use it to access the elements in the collection
476
- if isinstance(key, int):
477
- return self._elements[key]
478
- # If it's a slice we use it to support slice operations over the elements
479
- # in the collection
480
- elif isinstance(key, slice):
481
- return ElementCollection(self._elements[key])
482
-
483
- # If it's anything else (basically a string) we use it as a selector
484
- # TODO: Write tests!
485
- elements = self._element.querySelectorAll(key)
486
- return ElementCollection([Element(el) for el in elements])
487
-
488
- def __len__(self):
489
- return len(self._elements)
490
-
491
- def __eq__(self, obj):
492
- """Check if the element is the same as the other element by comparing
493
- the underlying JS element"""
494
- return isinstance(obj, ElementCollection) and obj._elements == self._elements
495
-
496
- def _get_attribute(self, attr, index=None):
497
- if index is None:
498
- return [getattr(el, attr) for el in self._elements]
499
-
500
- # As JQuery, when getting an attr, only return it for the first element
501
- return getattr(self._elements[index], attr)
502
-
503
- def _set_attribute(self, attr, value):
504
- for el in self._elements:
505
- setattr(el, attr, value)
506
-
507
- @property
508
- def html(self):
509
- return self._get_attribute("html")
510
-
511
- @html.setter
512
- def html(self, value):
513
- self._set_attribute("html", value)
514
-
515
- @property
516
- def value(self):
517
- return self._get_attribute("value")
518
-
519
- @value.setter
520
- def value(self, value):
521
- self._set_attribute("value", value)
522
-
523
- @property
524
- def children(self):
525
- return self._elements
526
-
527
- def __iter__(self):
528
- yield from self._elements
529
-
530
- def __repr__(self):
531
- return f"{self.__class__.__name__} (length: {len(self._elements)}) {self._elements}"
532
-
533
-
534
- class DomScope:
535
- def __getattr__(self, __name: str):
536
- element = document[f"#{__name}"]
537
- if element:
538
- return element[0]
539
-
540
-
541
- class PyDom(BaseElement):
542
- # Add objects we want to expose to the DOM namespace since this class instance is being
543
- # remapped as "the module" itself
544
- BaseElement = BaseElement
545
- Element = Element
546
- ElementCollection = ElementCollection
547
-
548
- def __init__(self):
549
- # PyDom is a special case of BaseElement where we don't want to create a new JS element
550
- # and it really doesn't have a need for styleproxy or parent to to call to __init__
551
- # (which actually fails in MP for some reason)
552
- self._js = document
553
- self._parent = None
554
- self._proxies = {}
555
- self.ids = DomScope()
556
- self.body = Element(document.body)
557
- self.head = Element(document.head)
558
-
559
- def create(self, type_, classes=None, html=None):
560
- return super().create(type_, is_child=False, classes=classes, html=html)
561
-
562
- def __getitem__(self, key):
563
- elements = self._js.querySelectorAll(key)
564
- if not elements:
565
- return None
566
- return ElementCollection([Element(el) for el in elements])
567
-
568
-
569
- dom = PyDom()
@@ -1 +0,0 @@
1
- from . import elements