@axel669/zephyr 0.1.1

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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +32 -0
  3. package/readme.md +44 -0
  4. package/src/avatar.svelte +54 -0
  5. package/src/badge.svelte +40 -0
  6. package/src/button.svelte +80 -0
  7. package/src/chip.svelte +53 -0
  8. package/src/composed/data-table.svelte +150 -0
  9. package/src/composed/entry-button.svelte +66 -0
  10. package/src/composed/toast/message.svelte +44 -0
  11. package/src/composed/toast.svelte +108 -0
  12. package/src/dialog.svelte +53 -0
  13. package/src/drawer.svelte +121 -0
  14. package/src/flex.svelte +57 -0
  15. package/src/grid.svelte +66 -0
  16. package/src/handler$.mjs +68 -0
  17. package/src/icon.svelte +34 -0
  18. package/src/index.mjs +85 -0
  19. package/src/inline-dialog.svelte +23 -0
  20. package/src/input/area.svelte +97 -0
  21. package/src/input/elems.mjs +7 -0
  22. package/src/input/email.svelte +102 -0
  23. package/src/input/file.svelte +81 -0
  24. package/src/input/number.svelte +102 -0
  25. package/src/input/password.svelte +99 -0
  26. package/src/input/search.svelte +102 -0
  27. package/src/input/text.svelte +102 -0
  28. package/src/link.svelte +62 -0
  29. package/src/modal.svelte +68 -0
  30. package/src/notification.svelte +57 -0
  31. package/src/paper.svelte +114 -0
  32. package/src/popover.svelte +60 -0
  33. package/src/progress.svelte +60 -0
  34. package/src/radio.svelte +82 -0
  35. package/src/screen.svelte +69 -0
  36. package/src/select.svelte +121 -0
  37. package/src/table.svelte +69 -0
  38. package/src/tabs.svelte +77 -0
  39. package/src/text.svelte +49 -0
  40. package/src/titlebar.svelte +69 -0
  41. package/src/toaster.svelte +42 -0
  42. package/src/toggle.svelte +90 -0
  43. package/src/tooltip.svelte +45 -0
  44. package/src/vars.mjs +61 -0
  45. package/src/wsx.mjs +45 -0
@@ -0,0 +1,68 @@
1
+ <script>
2
+ /*md
3
+ [@] Components/Modal
4
+
5
+ # Modal
6
+
7
+ The Modal component is used to control when content should be shown that
8
+ sits over the current content and prevents interaction with the content it
9
+ covers. It is primarily used to show drawers, dialogs, and sub screens.
10
+
11
+ The `show` function will display the component specified inside a ws-modal
12
+ element, and can be awaited for an optional return value. The component
13
+ displayed will be passed any props supplied to `show` and a `close` function
14
+ that can be called to hide the component again. `close` can also be given
15
+ an argument that will be used as the promise value from `show`.
16
+
17
+ ## Props
18
+ Because the Modal does not render content of its own, it does not support
19
+ any wind functions.
20
+
21
+ - ### component `Component`
22
+ The component to display when `show` is called
23
+
24
+ ## Functions
25
+ - ### `show(props?) -> Promise`
26
+ Shows the given component and passes any props provided.
27
+
28
+ ## Usage
29
+ ```svelte
30
+ <Modal component={CoolDialog} bind:this={dialog} />
31
+ <Button on:click={() => dialog.show({ a: 10, b: 12 })}>
32
+ Open Dialog
33
+ </Button>
34
+ ```
35
+ */
36
+
37
+ export let component
38
+
39
+ let modalProps = null
40
+ let resolver = null
41
+ let displayed = null
42
+
43
+ const close = (value) => {
44
+ resolver(value)
45
+ resolver = null
46
+ modalProps = null
47
+ }
48
+ const cancel = () => displayed.cancel?.()
49
+
50
+ export const show = (props) => new Promise(
51
+ (resolve) => {
52
+ modalProps = props ?? {}
53
+ resolver = resolve
54
+ }
55
+ )
56
+ </script>
57
+
58
+ {#if resolver !== null}
59
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
60
+ <ws-modal ws-x="$show" on:click={cancel}>
61
+ <svelte:component
62
+ bind:this={displayed}
63
+ this={component}
64
+ {...modalProps}
65
+ {close}
66
+ />
67
+ </ws-modal>
68
+ {/if}
@@ -0,0 +1,57 @@
1
+ <script>
2
+ /*md
3
+ [@] Components/Notification
4
+
5
+ # Notification
6
+
7
+ Displays a piece of content that stands out from the surrounding content and
8
+ can also be used for toast messages. Optionally has some interactable
9
+ elements embedded.
10
+
11
+ Expects 1-2 child nodes and will display them on the sides of the
12
+ notification with space between them. This means that reversing the order
13
+ of the child nodes will flip which sides they appear on.
14
+
15
+ ## Base
16
+ [Windstorm Notification](https://axel669.github.io/lib.windstorm/#components-notification)
17
+
18
+ ## Props
19
+ All [windstorm functions](https://axel669.github.io/lib.windstorm/#css-shorthands)
20
+ are supported.
21
+
22
+ - ### color
23
+ Sets `$color` and uses the fill style for coloration.
24
+
25
+ ## Usage
26
+ ```svelte
27
+ <Notification>
28
+ <Icon name="info-hexagon">
29
+ Some kind of information
30
+ </Icon>
31
+ </Notification>
32
+ <Notification color="danger">
33
+ <Icon name="alert-hexagon">
34
+ Something broke
35
+ </Icon>
36
+
37
+ <Button>
38
+ Cancel
39
+ </Button>
40
+ </Notification>
41
+ ```
42
+ */
43
+
44
+ import wsx from "./wsx.mjs"
45
+
46
+ export let color = false
47
+
48
+ $: wind = {
49
+ $color: color,
50
+ ...$$restProps,
51
+ }
52
+ </script>
53
+
54
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
55
+ <ws-notification use:wsx={wind} on:click>
56
+ <slot />
57
+ </ws-notification>
@@ -0,0 +1,114 @@
1
+ <script>
2
+ /*md
3
+ [@] Components/Containers/Paper
4
+
5
+ # Paper
6
+
7
+ The Paper component is a container that has slots for header and footer
8
+ content that is independant from the regular content scrolling. It also uses
9
+ Flex as a default layout for its slotted content, but allows any container
10
+ to be used as a layout without increasing the indentation of all the content
11
+ needlessly.
12
+
13
+ ## Base
14
+ [Windstorm Paper](https://axel669.github.io/lib.windstorm/#components-paper)
15
+
16
+ ## Props
17
+ All [windstorm functions](https://axel669.github.io/lib.windstorm/#css-shorthands)
18
+ are supported.
19
+
20
+ - ### color `string`
21
+ Sets `$color`
22
+ - ### card `bool`
23
+ Sets `@outline`
24
+ - ### square `bool`
25
+ Sets `r[0px]`
26
+ - ### layout `Component`
27
+ Sets the layout the card will use to display content. Default is Flex.
28
+ - ### scrollable `bool`
29
+ Sets `over[auto]` on the layout component
30
+ - ### lprops `Object`
31
+ An object with props to pass to the layout component
32
+
33
+ ## Slots
34
+ - ### header
35
+ The header content for the Paper. Header content does not scroll with
36
+ the regular content of the component (acts sticky)
37
+ - ### footer
38
+ The footer content for the Paper. Same scrolling properties as the
39
+ header slot
40
+ - ### content/default
41
+ The default slot (unnamed) for Paper will be rendered in the layout
42
+ component defined in the props. As an alternative, a content slot may
43
+ be used that will be rendered directly in the content slot of the Paper
44
+ without the layout component/props being rendered (useful when nesting
45
+ papers for example)
46
+
47
+ ## Usage
48
+ ```svelte
49
+ <Paper>
50
+ <Titlebar slot="header">
51
+ <Text slot="title" title>
52
+ Some Title
53
+ </Text>
54
+ </Titlebar>
55
+
56
+ <div>Content 1</div>
57
+ <div>Content 2</div>
58
+ <div>Content 3</div>
59
+ </Paper>
60
+ <Paper>
61
+ <Titlebar slot="header">
62
+ <Text slot="title" title>
63
+ Some Screen
64
+ </Text>
65
+ </Titlebar>
66
+
67
+ <Paper slot="content">
68
+ <Tabs slot="header" {options} bind:value />
69
+
70
+ <div>Content 1</div>
71
+ <div>Content 2</div>
72
+ <div>Content 3</div>
73
+ </Paper>
74
+
75
+ <div slot="footer">
76
+ Wat
77
+ </div>
78
+ </Paper>
79
+ ```
80
+ */
81
+
82
+ import Flex from "./flex.svelte"
83
+ import wsx from "./wsx.mjs"
84
+
85
+ export let color
86
+ export let card = false
87
+ export let square = false
88
+ export let layout = Flex
89
+ export let scrollable = false
90
+ export let lprops = {}
91
+
92
+ $: props = {
93
+ over: scrollable ? "auto" : false,
94
+ ...lprops,
95
+ }
96
+ $: wind = {
97
+ $color: color,
98
+ "@outline": card,
99
+ r: square && "0px",
100
+ ...$$restProps
101
+ }
102
+ </script>
103
+
104
+ <ws-paper use:wsx={wind}>
105
+ <slot name="header" />
106
+ {#if $$slots.content}
107
+ <slot name="content" slot="content" />
108
+ {:else}
109
+ <svelte:component this={layout} {...props} slot="content">
110
+ <slot />
111
+ </svelte:component>
112
+ {/if}
113
+ <slot name="footer" />
114
+ </ws-paper>
@@ -0,0 +1,60 @@
1
+ <script>
2
+ /*md
3
+ [@] Components/Containers/Popover
4
+
5
+ # Popover
6
+
7
+ The Popover component is for displaying content over something without it
8
+ being modal (you can interact with things under it still).
9
+
10
+ ## Base
11
+ [Windstorm Popover](https://axel669.github.io/lib.windstorm/#components-popover)
12
+
13
+ ## Props
14
+ All [windstorm functions](https://axel669.github.io/lib.windstorm/#css-shorthands)
15
+ are supported.
16
+
17
+ ## Variables
18
+ - show
19
+ - hide
20
+ The Popover exposes 2 variables for use: `show` and `hide`. These functions
21
+ are given to the content so that it can control when to display the popover
22
+ content without needing to bind to the parent.
23
+
24
+ ## Usage
25
+ ```svelte
26
+ <Popover let:show let:hide>
27
+ <Button on:click={show}>
28
+ Show
29
+ </Button>
30
+ <div ws-x="inset-x[0px] y[0px] h[100px] bg[teal]" slot="content">
31
+ <Button on:click={hide}>
32
+ Hide
33
+ </Button>
34
+ </div>
35
+ </Popover>
36
+ ```
37
+ */
38
+
39
+ import { fade } from "svelte/transition"
40
+ import wsx from "./wsx.mjs"
41
+
42
+ let visible = false
43
+ const show = () => visible = true
44
+ const hide = () => visible = false
45
+ const anim = { duration: 200 }
46
+
47
+ $: wind = {
48
+ $show: true,
49
+ ...$$restProps,
50
+ }
51
+ </script>
52
+
53
+ <ws-popover use:wsx={wind}>
54
+ <slot {show} />
55
+ {#if visible}
56
+ <wind-content ws-x="slot[content] inset[0px]" transition:fade={anim}>
57
+ <slot name="content" {hide} />
58
+ </wind-content>
59
+ {/if}
60
+ </ws-popover>
@@ -0,0 +1,60 @@
1
+ <script>
2
+ /*md
3
+ [@] Components/Progress
4
+
5
+ # Progress
6
+
7
+ Displays progress bar with an optional label.
8
+
9
+ ## Base
10
+ [Windstorm Progress Bar](https://axel669.github.io/lib.windstorm/#components-progress-bar)
11
+
12
+ ## Props
13
+ All [windstorm functions](https://axel669.github.io/lib.windstorm/#css-shorthands)
14
+ are supported.
15
+
16
+ - ### color `string`
17
+ Sets `$color`
18
+ - ### label `string`
19
+ The label for the bar
20
+ - ### max `number`
21
+ Sets the max value for the progress bar. Default is 1
22
+ - ### outline `bool`
23
+ Sets `@outline`
24
+ - ### row `bool`
25
+ Sets `$row`
26
+ - ### value `number`
27
+ Sets the value of the progress bar. Combined with max to determine how
28
+ filled the bar is.
29
+
30
+ ## Usage
31
+ ```svelte
32
+ <Progress color="primary" value={0.1} />
33
+ <Progress color="primary" value={5} max={10} />
34
+ ```
35
+ */
36
+
37
+ import wsx from "./wsx.mjs"
38
+
39
+ export let value = 0
40
+ export let max = 1
41
+ export let color = "default"
42
+ export let outline = false
43
+ export let row = false
44
+ export let label = null
45
+
46
+ $: wind = {
47
+ "@progress": true,
48
+ "@outline": outline,
49
+ $color: color,
50
+ $row: row,
51
+ ...$$restProps,
52
+ }
53
+ </script>
54
+
55
+ <label use:wsx={wind}>
56
+ {#if label}
57
+ <span ws-x="$text t-ws[nowrap]">{label}</span>
58
+ {/if}
59
+ <progress {value} {max} />
60
+ </label>
@@ -0,0 +1,82 @@
1
+ <script>
2
+ /*md
3
+ [@] Components/Input/Radio
4
+
5
+ # Radio
6
+
7
+ The radio component displays a list of radio options and provides an
8
+ interface to control which one is selected and react to changes in the
9
+ selected value. It also uses a layout component to decide how the options
10
+ are presented.
11
+
12
+ ## Base
13
+ Each radio item rendered is a
14
+ [Windstorm Labeled Toggle](https://axel669.github.io/lib.windstorm/#components-labeled toggle)
15
+ that uses a radio input.
16
+
17
+ ## Props
18
+ All [windstorm functions](https://axel669.github.io/lib.windstorm/#css-shorthands)
19
+ are supported.
20
+
21
+ Unlike the Paper, the wind functions passed as props to the Radio component
22
+ are passed directly to the layout specified because the Radio does not have
23
+ a container element of its own.
24
+
25
+ - ### layout `Component`
26
+ The component to use for layout out the options. Default is Flex.
27
+ - ### options `Array[Object]`
28
+ An array of objects that defines the options to show. Details below.
29
+ - ### value `any`
30
+ The value currently selected. Can be bound to react to selections or
31
+ change the current selection.
32
+
33
+ ## Options
34
+ Each object in the options array supplied should have a `value` and `label`
35
+ prop specified. Additionally, any wind functions that should be applied to
36
+ the option can be passed as additional props.
37
+
38
+ Labels should be strings, but values can be any type as long as they are
39
+ unique in the list.
40
+
41
+ ```js
42
+ const options = [
43
+ { label: "first", value: 1 },
44
+ { label: "second", value: [] },
45
+ { label: "third", value: "3", $color: "warning" }
46
+ ]
47
+ ```
48
+
49
+ ## Usage
50
+ ```svelte
51
+ <Radio {options} bind:value />
52
+ <Radio {options} bind:value={other} layout={Grid} gr-col="1fr 1fr" />
53
+ ```
54
+ */
55
+
56
+ import asuid from "@labyrinthos/asuid"
57
+
58
+ import wsx from "./wsx.mjs"
59
+ import { handler$ } from "./handler$.mjs"
60
+
61
+ import Flex from "./flex.svelte"
62
+
63
+ export let options = []
64
+ export let layout = Flex
65
+ export let value
66
+
67
+ $: valueIndex = options.findIndex(item => item.value === value)
68
+ const set = handler$(
69
+ (newValue) => value = newValue
70
+ )
71
+
72
+ const name = asuid()
73
+ </script>
74
+
75
+ <svelte:component this={layout} {...$$restProps}>
76
+ {#each options as {value, label, ...wind}, index (value)}
77
+ <label use:wsx={{"@toggle": true, ...wind}}>
78
+ <span>{label}</span>
79
+ <input type="radio" on:input={set(value)} {name} checked={index === valueIndex} />
80
+ </label>
81
+ {/each}
82
+ </svelte:component>
@@ -0,0 +1,69 @@
1
+ <script context="module">
2
+ const ctxStack = Symbol("stack context")
3
+ const ctxClose = Symbol("close context")
4
+ </script>
5
+
6
+ <script>
7
+ /*md
8
+ [@] Components/Containers/Screen
9
+
10
+ # Screen
11
+
12
+ The screen is a container component that is used for displaying content
13
+ over its parent and stacks as more screens are displayed. Screens that stack
14
+ on top of a parent screen use the Modal to control when they are shown and
15
+ pass values back from child screens. The stacking effect of the Screen is
16
+ controlled by internally tracked svelte context, so no manual effort is
17
+ required to get stacking screens to have the effect.
18
+
19
+ ## Base
20
+ [Windstorm Screen](https://axel669.github.io/lib.windstorm/#components-screen)
21
+
22
+ ## Props
23
+ All [windstorm functions](https://axel669.github.io/lib.windstorm/#css-shorthands)
24
+ are supported, but they probably won't help much with this component.
25
+
26
+ - ### width `string`
27
+ The width of the screen before stack-based padding is applied
28
+
29
+ ## Usage
30
+ `cool-screen.svelte`
31
+ ```svelte
32
+ <Screen width="70%">
33
+ <Paper>
34
+ This screen is pretty cool I swear
35
+ <Button on:click={() => close("hi")}>
36
+ Close
37
+ </Button>
38
+ </Paper>
39
+ </Screen>
40
+ ```
41
+ */
42
+
43
+ import { getContext, setContext } from "svelte"
44
+ import { fly } from "svelte/transition"
45
+
46
+ import wsx from "./wsx.mjs"
47
+ import vars from "./vars.mjs"
48
+
49
+ export let width = false
50
+
51
+ const stack = getContext(ctxStack) ?? 0
52
+ const animation = {
53
+ y: window.innerHeight,
54
+ duration: 350
55
+ }
56
+
57
+ setContext(ctxStack, stack + 1)
58
+
59
+ $: wind = {
60
+ "&stack": stack.toString(),
61
+ "&screen-width": width,
62
+ "bg-c": "transparent",
63
+ ...$$restProps
64
+ }
65
+ </script>
66
+
67
+ <ws-screen use:wsx={wind} transition:fly={animation}>
68
+ <slot />
69
+ </ws-screen>
@@ -0,0 +1,121 @@
1
+ <script context="module">
2
+ const groupOptions = (options) => options.reduce(
3
+ ({grouped, target = grouped}, item, pos) => {
4
+ const opt = { ... item, pos }
5
+ if (item.group === undefined) {
6
+ target.push(opt)
7
+ return { grouped, target }
8
+ }
9
+ const next = [opt]
10
+ grouped.push(next)
11
+ return { grouped, target: next }
12
+ },
13
+ { grouped: [] }
14
+ ).grouped
15
+ </script>
16
+
17
+ <script>
18
+ /*md
19
+ [@] Components/Input/Select
20
+
21
+ # Select
22
+
23
+ A wrapper for select elements to make them look nicer.
24
+
25
+ ## Base
26
+ [Windstorm Labeled Control](https://axel669.github.io/lib.windstorm/#components-labeled control)
27
+ using a `<select>`
28
+
29
+ ## Props
30
+ All [windstorm functions](https://axel669.github.io/lib.windstorm/#css-shorthands)
31
+ are supported.
32
+
33
+ - ### color `string`
34
+ Sets `$color`
35
+ - ### label `string`
36
+ The text to display as the label
37
+ - ### options `Array[Object]`
38
+ An array of options and groups to show in the select. Details below
39
+ - ### outline `bool`
40
+ Sets `@outline`
41
+ - ### value `any`
42
+ The value currently selected. Can be bound to react to selections or
43
+ change the current selection.
44
+
45
+ ## Options
46
+ Each object in the options provided should be in one of 2 forms:
47
+ - `{ label, value }`
48
+ - `{ group }`
49
+
50
+ Items with a label and value are displayed as <option> tags in the select.
51
+ Items with a group defined are used to denote <optgroup> tags. Each option
52
+ after a `{ group }` item until another group item is seen (or the end of the
53
+ array) is put into the optgroup defined by the group item.
54
+
55
+ Labels and group names should be strings, but values can be any type.
56
+
57
+ ## Usage
58
+ ```js
59
+ // Results in:
60
+ // <option>first</option>
61
+ // <optgroup label="Not Numbers">
62
+ // <option>second</option>
63
+ // <option>third</option>
64
+ // </optgroup>
65
+ const options = [
66
+ { label: "first", value: 1 },
67
+ { group: "Not numbers" },
68
+ { label: "second", value: "two" },
69
+ { label: "third", value: [3] },
70
+ ]
71
+ ```
72
+ ```svelte
73
+ <Select {options} bind:value label="Blep" />
74
+ <Select {options} bind:value color="warning" label="Why" />
75
+ ```
76
+ */
77
+
78
+ import wsx from "./wsx.mjs"
79
+
80
+ export let options = []
81
+ export let value
82
+ export let color = "default"
83
+ export let outline = false
84
+ export let label = null
85
+
86
+ const update = (evt) => value = options[evt.target.value].value
87
+
88
+ $: indexValue = options.findIndex(item => item.value === value)
89
+
90
+ $: grouped = groupOptions(options)
91
+
92
+ $: wind = {
93
+ "@control": true,
94
+ "@outline": outline,
95
+ $color: color,
96
+ ...$$restProps,
97
+ }
98
+ </script>
99
+
100
+ <label use:wsx={wind}>
101
+ {#if label}
102
+ <span ws-x="$text">{label}</span>
103
+ {/if}
104
+ <select value={indexValue} on:input={update}>
105
+ {#each grouped as item, index}
106
+ {#if Array.isArray(item) === false}
107
+ <option value={item.pos}>
108
+ {item.label}
109
+ </option>
110
+ {:else}
111
+ <optgroup label={item[0].group}>
112
+ {#each item.slice(1) as subitem, subindex}
113
+ <option value={subitem.pos}>
114
+ {subitem.label}
115
+ </option>
116
+ {/each}
117
+ </optgroup>
118
+ {/if}
119
+ {/each}
120
+ </select>
121
+ </label>
@@ -0,0 +1,69 @@
1
+ <script>
2
+ /*md
3
+ [@] Components/Data Display/Table
4
+
5
+ # Table
6
+
7
+ Displays data using html tables with some nice styling. This version of the
8
+ table is fairly simple and intended for basic data displays. For more
9
+ complex data displays the DataTable should be used.
10
+
11
+ ## Base
12
+ [Windstorm Table](https://axel669.github.io/lib.windstorm/#components-table)
13
+
14
+ ## Props
15
+ All [windstorm functions](https://axel669.github.io/lib.windstorm/#css-shorthands)
16
+ are supported.
17
+
18
+ - ### color `string`
19
+ Sets `$color`
20
+ - ### cols `Array`
21
+ The columns that should be displayed. Should be in the format of
22
+ `{ label, width? }`
23
+ - ### data `Array[Array]`
24
+ The data to display. The data should be an array where each item is a
25
+ row, and each item in the row is the value for a column.
26
+ - ### cellWSX `function`
27
+ If given, this function will be called for each cell, and should return
28
+ an object that will be passed to the `wsx` action for each <td>
29
+ generated. The function is passed `(cellValue, rowNum, colNum)`.
30
+ - ### rowWSX `function`
31
+ If given, this function will be called for each row, and should return
32
+ an object that will be passed to the `wsx` action for each <tr>
33
+ generated. The function is passed `(rowValue, rowNum)`.
34
+ */
35
+
36
+ import wsx from "./wsx.mjs"
37
+
38
+ export let color = false
39
+ export let data = []
40
+ export let cols = []
41
+ export let rowWSX = null
42
+ export let cellWSX = null
43
+
44
+ $: wind = {
45
+ $color: color,
46
+ ...$$restProps,
47
+ }
48
+ </script>
49
+
50
+ <table use:wsx={wind}>
51
+ <thead>
52
+ <tr>
53
+ {#each cols as col}
54
+ <th use:wsx={{ w: col.width }}>{col.label}</th>
55
+ {/each}
56
+ </tr>
57
+ </thead>
58
+ <tbody>
59
+ {#each data as row, rowNum}
60
+ <tr use:wsx={rowWSX?.(row, rowNum)}>
61
+ {#each row as cell, colNum}
62
+ <td use:wsx={cellWSX?.(cell, rowNum, colNum)}>
63
+ {cell}
64
+ </td>
65
+ {/each}
66
+ </tr>
67
+ {/each}
68
+ </tbody>
69
+ </table>