@versatiles/svelte 1.1.2 → 2.0.0

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 (42) hide show
  1. package/dist/components/BasicMap/BasicMap.svelte +4 -4
  2. package/dist/components/BasicMap/BasicMap.svelte.d.ts +2 -2
  3. package/dist/components/MapEditor/MapEditor.svelte +16 -3
  4. package/dist/components/MapEditor/components/Editor.svelte +46 -40
  5. package/dist/components/MapEditor/components/Editor.svelte.d.ts +1 -1
  6. package/dist/components/MapEditor/components/EditorFill.svelte +7 -9
  7. package/dist/components/MapEditor/components/EditorStroke.svelte +9 -11
  8. package/dist/components/MapEditor/components/EditorSymbol.svelte +15 -21
  9. package/dist/components/MapEditor/components/InputRow.svelte +34 -0
  10. package/dist/components/MapEditor/components/InputRow.svelte.d.ts +9 -0
  11. package/dist/components/MapEditor/components/Sidebar.svelte +115 -108
  12. package/dist/components/MapEditor/components/SidebarPanel.svelte +92 -0
  13. package/dist/components/MapEditor/components/SidebarPanel.svelte.d.ts +10 -0
  14. package/dist/components/MapEditor/components/SymbolSelector.svelte +7 -15
  15. package/dist/components/MapEditor/lib/element/abstract.d.ts +3 -3
  16. package/dist/components/MapEditor/lib/element/abstract.js +1 -1
  17. package/dist/components/MapEditor/lib/element/line.d.ts +3 -4
  18. package/dist/components/MapEditor/lib/element/line.js +0 -1
  19. package/dist/components/MapEditor/lib/element/marker.d.ts +4 -4
  20. package/dist/components/MapEditor/lib/element/polygon.d.ts +3 -3
  21. package/dist/components/MapEditor/lib/geometry_manager.d.ts +20 -10
  22. package/dist/components/MapEditor/lib/geometry_manager.js +44 -52
  23. package/dist/components/MapEditor/lib/map_layer/abstract.d.ts +3 -2
  24. package/dist/components/MapEditor/lib/map_layer/abstract.js +3 -0
  25. package/dist/components/MapEditor/lib/map_layer/fill.d.ts +4 -3
  26. package/dist/components/MapEditor/lib/map_layer/fill.js +9 -8
  27. package/dist/components/MapEditor/lib/map_layer/line.d.ts +4 -3
  28. package/dist/components/MapEditor/lib/map_layer/line.js +15 -14
  29. package/dist/components/MapEditor/lib/map_layer/symbol.d.ts +5 -4
  30. package/dist/components/MapEditor/lib/map_layer/symbol.js +28 -46
  31. package/dist/components/MapEditor/lib/state/constants.d.ts +4 -0
  32. package/dist/components/MapEditor/lib/state/constants.js +22 -0
  33. package/dist/components/MapEditor/lib/state/manager.d.ts +16 -0
  34. package/dist/components/MapEditor/lib/state/manager.js +76 -0
  35. package/dist/components/MapEditor/lib/state/reader.d.ts +21 -14
  36. package/dist/components/MapEditor/lib/state/reader.js +259 -142
  37. package/dist/components/MapEditor/lib/state/types.d.ts +27 -12
  38. package/dist/components/MapEditor/lib/state/writer.d.ts +18 -14
  39. package/dist/components/MapEditor/lib/state/writer.js +182 -169
  40. package/dist/components/MapEditor/lib/utils.d.ts +2 -5
  41. package/dist/components/MapEditor/lib/utils.js +0 -19
  42. package/package.json +19 -19
@@ -17,8 +17,8 @@
17
17
  styleOptions?: Parameters<typeof getMapStyle>[1];
18
18
  mapOptions?: Partial<MapOptions>;
19
19
  map?: maplibre.Map;
20
- onMapInit?: (map: maplibre.Map) => void;
21
- onMapLoad?: (map: maplibre.Map) => void;
20
+ onMapInit?: (map: maplibre.Map, maplibre: typeof import('maplibre-gl')) => void;
21
+ onMapLoad?: (map: maplibre.Map, maplibre: typeof import('maplibre-gl')) => void;
22
22
  } = $props();
23
23
 
24
24
  let container: HTMLDivElement;
@@ -48,10 +48,10 @@
48
48
  ...mapOptions
49
49
  });
50
50
 
51
- if (onMapInit) onMapInit(map);
51
+ if (onMapInit) onMapInit(map, maplibre);
52
52
 
53
53
  map.on('load', () => {
54
- if (onMapLoad) onMapLoad(map!);
54
+ if (onMapLoad) onMapLoad(map!, maplibre);
55
55
  });
56
56
  }
57
57
  </script>
@@ -7,8 +7,8 @@ type $$ComponentProps = {
7
7
  styleOptions?: Parameters<typeof getMapStyle>[1];
8
8
  mapOptions?: Partial<MapOptions>;
9
9
  map?: maplibre.Map;
10
- onMapInit?: (map: maplibre.Map) => void;
11
- onMapLoad?: (map: maplibre.Map) => void;
10
+ onMapInit?: (map: maplibre.Map, maplibre: typeof import('maplibre-gl')) => void;
11
+ onMapLoad?: (map: maplibre.Map, maplibre: typeof import('maplibre-gl')) => void;
12
12
  };
13
13
  declare const BasicMap: import("svelte").Component<$$ComponentProps, {}, "map">;
14
14
  type BasicMap = ReturnType<typeof BasicMap>;
@@ -15,25 +15,34 @@
15
15
  let map: MaplibreMapType | undefined = $state();
16
16
  let geometryManager: GeometryManager | undefined = $state();
17
17
 
18
- function onMapInit(_map: MaplibreMapType) {
18
+ function onMapInit(_map: MaplibreMapType, maplibre: typeof import('maplibre-gl')) {
19
19
  map = _map;
20
+ map.addControl(new maplibre.AttributionControl({ compact: true }), 'bottom-left');
21
+
20
22
  map.on('load', async () => {
21
23
  const { GeometryManager } = await import('./lib/geometry_manager.js');
22
24
  geometryManager = new GeometryManager(map!);
25
+ const hash = location.hash.slice(1);
26
+ if (hash) geometryManager.state.setHash(hash);
27
+ addEventListener('hashchange', () => geometryManager!.state.setHash(location.hash.slice(1)));
23
28
  });
24
29
  }
25
30
  </script>
26
31
 
27
32
  <div class="page">
28
33
  <div class="container">
29
- <BasicMap {onMapInit} styleOptions={{ disableDarkMode: true }}></BasicMap>
34
+ <BasicMap
35
+ {onMapInit}
36
+ styleOptions={{ disableDarkMode: true }}
37
+ mapOptions={{ attributionControl: false }}
38
+ ></BasicMap>
30
39
  </div>
31
40
  {#if showSidebar && geometryManager}
32
41
  <Sidebar {geometryManager} width={200} />
33
42
 
34
43
  <style>
35
44
  .page .container {
36
- width: calc(100% - 200px);
45
+ width: 100%;
37
46
  position: absolute;
38
47
  top: 0;
39
48
  left: 0;
@@ -50,4 +59,8 @@
50
59
  position: relative;
51
60
  min-height: 6em;
52
61
  }
62
+ :global(.maplibregl-ctrl-attrib) {
63
+ display: flex;
64
+ align-items: center;
65
+ }
53
66
  </style>
@@ -6,49 +6,55 @@
6
6
  import { LineElement } from '../lib/element/line.js';
7
7
  import { MarkerElement } from '../lib/element/marker.js';
8
8
  import { PolygonElement } from '../lib/element/polygon.js';
9
+ import InputRow from './InputRow.svelte';
10
+ import SidebarPanel from './SidebarPanel.svelte';
11
+ import { writable } from 'svelte/store';
9
12
 
10
- const { element }: { element: AbstractElement } = $props();
13
+ const { element }: { element: AbstractElement | undefined } = $props();
11
14
 
12
- let strokeVisible = $state(false);
13
-
14
- if (element instanceof PolygonElement) {
15
- element.strokeLayer.visible.subscribe((value) => (strokeVisible = value));
16
- }
15
+ const noElement = $derived(!element);
16
+ const strokeVisible = $derived.by(() => {
17
+ if (element instanceof PolygonElement) return element.strokeLayer.visible;
18
+ return writable(false);
19
+ });
17
20
  </script>
18
21
 
19
- {#if element instanceof MarkerElement}
20
- <h2>Marker</h2>
21
- <EditorSymbol layer={element.layer} />
22
- {/if}
23
- {#if element instanceof LineElement}
24
- <h2>Line Stroke</h2>
25
- <EditorStroke layer={element.layer} />
26
- {/if}
27
- {#if element instanceof PolygonElement}
28
- <h2>Polygon Fill</h2>
29
- <EditorFill layer={element.fillLayer} />
30
- <h2>Polygon Stroke</h2>
22
+ {#key element}
23
+ <SidebarPanel title="Style" disabled={noElement}>
24
+ <div class="style-editor">
25
+ {#if element instanceof MarkerElement}
26
+ <EditorSymbol layer={element.layer} />
27
+ {/if}
28
+ {#if element instanceof LineElement}
29
+ <EditorStroke layer={element.layer} />
30
+ {/if}
31
+ {#if element instanceof PolygonElement}
32
+ <EditorFill layer={element.fillLayer} />
33
+ <hr />
31
34
 
32
- <div class="row-input">
33
- <label for="showStroke">Outline:</label>
34
- <input
35
- id="showStroke"
36
- type="checkbox"
37
- bind:checked={
38
- () => strokeVisible, (v) => (element as PolygonElement).strokeLayer.visible.set(v)
39
- }
40
- />
41
- </div>
35
+ <InputRow id="showStroke" label="Draw Outline">
36
+ <input id="showStroke" type="checkbox" bind:checked={$strokeVisible} />
37
+ </InputRow>
42
38
 
43
- {#if strokeVisible}
44
- <EditorStroke layer={element.strokeLayer} />
45
- {/if}
46
- {/if}
47
- {#if element instanceof PolygonElement || element instanceof LineElement}
48
- <h2>Shape</h2>
49
- <p>
50
- Drag points to move.<br />Drag a midpoint to add.<br />Shift-click to delete a point.
51
- </p>
52
- {/if}
53
- <h2>Actions</h2>
54
- <input type="button" value="Delete" onclick={() => element!.delete()} />
39
+ {#if $strokeVisible}
40
+ <EditorStroke layer={element.strokeLayer} />
41
+ {/if}
42
+ {/if}
43
+ {#if element instanceof PolygonElement || element instanceof LineElement}
44
+ <hr />
45
+ <p>
46
+ Drag points to move.<br />Drag a midpoint to add.<br />Shift-click to delete a point.
47
+ </p>
48
+ {/if}
49
+ </div>
50
+ </SidebarPanel>
51
+ {/key}
52
+
53
+ <style>
54
+ .style-editor {
55
+ :global(p) {
56
+ opacity: 0.5;
57
+ margin: 0.5em 0 1em;
58
+ }
59
+ }
60
+ </style>
@@ -1,6 +1,6 @@
1
1
  import type { AbstractElement } from '../lib/element/abstract.js';
2
2
  type $$ComponentProps = {
3
- element: AbstractElement;
3
+ element: AbstractElement | undefined;
4
4
  };
5
5
  declare const Editor: import("svelte").Component<$$ComponentProps, {}, "">;
6
6
  type Editor = ReturnType<typeof Editor>;
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { fillPatterns, type MapLayerFill } from '../lib/map_layer/fill.js';
3
+ import InputRow from './InputRow.svelte';
3
4
 
4
5
  const { layer }: { layer: MapLayerFill } = $props();
5
6
 
@@ -8,21 +9,18 @@
8
9
  let opacity = $state(layer.opacity);
9
10
  </script>
10
11
 
11
- <div class="row-input">
12
- <label for="color">Color:</label>
12
+ <InputRow label="Color" id="color">
13
13
  <input id="color" type="color" bind:value={$color} />
14
- </div>
14
+ </InputRow>
15
15
 
16
- <div class="row-input">
17
- <label for="pattern">Pattern:</label>
16
+ <InputRow label="Pattern" id="pattern">
18
17
  <select id="pattern" bind:value={$pattern}>
19
18
  {#each fillPatterns as [index, fill] (index)}
20
19
  <option value={index}>{fill.name}</option>
21
20
  {/each}
22
21
  </select>
23
- </div>
22
+ </InputRow>
24
23
 
25
- <div class="row-input">
26
- <label for="opacity">Opacity:</label>
24
+ <InputRow label="Opacity" id="opacity">
27
25
  <input id="opacity" type="range" min="0" max="1" step="0.02" bind:value={$opacity} />
28
- </div>
26
+ </InputRow>
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { dashArrays, MapLayerLine } from '../lib/map_layer/line.js';
3
+ import InputRow from './InputRow.svelte';
3
4
 
4
5
  const { layer }: { layer: MapLayerLine } = $props();
5
6
 
@@ -8,21 +9,18 @@
8
9
  let dashed = $state(layer.dashed);
9
10
  </script>
10
11
 
11
- <div class="row-input">
12
- <label for="dashed">Dashed:</label>
12
+ <InputRow id="color" label="Color">
13
+ <input id="color" type="color" bind:value={$color} />
14
+ </InputRow>
15
+
16
+ <InputRow id="dashed" label="Dashed">
13
17
  <select id="dashed" bind:value={$dashed}>
14
18
  {#each dashArrays as [index, dash] (index)}
15
19
  <option value={index}>{dash.name}</option>
16
20
  {/each}
17
21
  </select>
18
- </div>
19
-
20
- <div class="row-input">
21
- <label for="color">Color:</label>
22
- <input id="color" type="color" bind:value={$color} />
23
- </div>
22
+ </InputRow>
24
23
 
25
- <div class="row-input">
26
- <label for="width">Width:</label>
24
+ <InputRow id="width" label="Width">
27
25
  <input id="width" type="range" min="0.5" max="5" step="0.5" bind:value={$width} />
28
- </div>
26
+ </InputRow>
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { labelPositions, MapLayerSymbol } from '../lib/map_layer/symbol.js';
3
+ import InputRow from './InputRow.svelte';
3
4
  import SymbolSelector from './SymbolSelector.svelte';
4
5
 
5
6
  const { layer }: { layer: MapLayerSymbol } = $props();
@@ -13,41 +14,34 @@
13
14
  let size = $state(layer.size);
14
15
  </script>
15
16
 
16
- <div class="row-input">
17
- <div class="label">Symbol:</div>
17
+ <InputRow id="label" label="Symbol">
18
18
  <SymbolSelector bind:symbolIndex={$symbolIndex} symbolLibrary={layer.manager.symbolLibrary} />
19
- </div>
19
+ </InputRow>
20
20
 
21
- <div class="row-input">
22
- <label for="color">Color:</label>
21
+ <InputRow id="color" label="Color">
23
22
  <input id="color" type="color" bind:value={$color} />
24
- </div>
23
+ </InputRow>
25
24
 
26
- <div class="row-input">
27
- <label for="size">Size:</label>
25
+ <InputRow id="size" label="Size">
28
26
  <input id="size" type="range" min="0.5" max="3" step="0.1" bind:value={$size} />
29
- </div>
27
+ </InputRow>
30
28
 
31
- <div class="row-input">
32
- <label for="rotate">Rotation:</label>
29
+ <InputRow id="rotate" label="Rotation">
33
30
  <input id="rotate" type="range" min="-180" max="180" step="15" bind:value={$rotate} />
34
- </div>
31
+ </InputRow>
35
32
 
36
- <div class="row-input">
37
- <label for="halo">Halo:</label>
33
+ <InputRow id="halo" label="Halo">
38
34
  <input id="halo" type="range" min="0" max="3" step="0.5" bind:value={$halo} />
39
- </div>
35
+ </InputRow>
40
36
 
41
- <div class="row-input">
42
- <label for="label">Label:</label>
37
+ <InputRow id="label" label="Label">
43
38
  <input id="label" type="label" bind:value={$label} />
44
- </div>
39
+ </InputRow>
45
40
 
46
- <div class="row-input">
47
- <label for="labelAlign">Align Label:</label>
41
+ <InputRow id="labelAlign" label="Align Label">
48
42
  <select id="labelAlign" bind:value={$labelAlign}>
49
43
  {#each labelPositions as { index, name } (index)}
50
44
  <option value={index}>{name}</option>
51
45
  {/each}
52
46
  </select>
53
- </div>
47
+ </InputRow>
@@ -0,0 +1,34 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ const { children, label, id }: { children: Snippet; label: string; id: string } = $props();
5
+ </script>
6
+
7
+ <div class="row">
8
+ <label for={id}>{label}</label>
9
+ {@render children()}
10
+ </div>
11
+
12
+ <style>
13
+ .row {
14
+ margin: var(--gap) 0 var(--gap);
15
+ display: flex;
16
+ justify-content: space-between;
17
+ align-items: center;
18
+ color: var(--fg-color);
19
+ & > :global(label) {
20
+ flex-grow: 0;
21
+ opacity: 0.5;
22
+ }
23
+ & > :global(button),
24
+ & > :global(input),
25
+ & > :global(select) {
26
+ box-sizing: border-box;
27
+ width: 60%;
28
+ flex-grow: 0;
29
+ }
30
+ & > :global(input[type='checkbox']) {
31
+ width: auto;
32
+ }
33
+ }
34
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ children: Snippet;
4
+ label: string;
5
+ id: string;
6
+ };
7
+ declare const InputRow: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type InputRow = ReturnType<typeof InputRow>;
9
+ export default InputRow;
@@ -1,9 +1,13 @@
1
1
  <script lang="ts">
2
2
  import Editor from './Editor.svelte';
3
3
  import type { GeometryManager } from '../lib/geometry_manager.js';
4
+ import SidebarPanel from './SidebarPanel.svelte';
4
5
 
5
6
  const { geometryManager, width }: { geometryManager: GeometryManager; width: number } = $props();
6
7
 
8
+ let history = geometryManager.state;
9
+ let undoEnabled = $state(geometryManager.state.undoEnabled);
10
+ let redoEnabled = $state(geometryManager.state.redoEnabled);
7
11
  let activeElement = geometryManager.selectedElement;
8
12
 
9
13
  function importGeoJSON() {
@@ -41,137 +45,140 @@
41
45
  a.click();
42
46
  URL.revokeObjectURL(url);
43
47
  }
48
+
49
+ function getLink() {
50
+ const url = new URL(window.location.href);
51
+ url.hash = geometryManager.state.getHash();
52
+ return url.href;
53
+ }
54
+
55
+ function copyLink() {
56
+ navigator.clipboard.writeText(getLink()).then(
57
+ () => alert('Link copied to clipboard!'),
58
+ () => alert('Failed to copy link. Please try again.')
59
+ );
60
+ }
61
+
62
+ function copyEmbedCode() {
63
+ const html = `<iframe style="width:100%; height:60vh" src="${getLink()}"></iframe>`;
64
+ navigator.clipboard.writeText(html).then(
65
+ () => alert('Embed code copied to clipboard!'),
66
+ () => alert('Failed to copy embed code. Please try again.')
67
+ );
68
+ }
44
69
  </script>
45
70
 
46
- <div class="sidebar" style="--gap: 10px;width: {width}px;">
47
- <div style="min-height: calc(100vh - 2em);">
48
- <div class="label">GeoJSON:</div>
49
- <div class="row-flex">
50
- <input type="button" value="Import" onclick={importGeoJSON} />
51
- <input type="button" value="Export" onclick={exportGeoJSON} />
52
- </div>
53
- <hr />
54
- <div class="label">Add new:</div>
55
- <div class="row-flex">
56
- <input
57
- type="button"
58
- value="Marker"
59
- onclick={() => activeElement.set(geometryManager.addNewMarker())}
60
- />
61
- <input
62
- type="button"
63
- value="Line"
64
- onclick={() => activeElement.set(geometryManager.addNewLine())}
65
- />
66
- <input
67
- type="button"
68
- value="Polygon"
69
- onclick={() => activeElement.set(geometryManager.addNewPolygon())}
70
- />
71
+ <div class="sidebar" style="width: {width}px;">
72
+ <div style="margin-bottom: 36px;">
73
+ <div class="flex flex-two">
74
+ <button onclick={() => history.undo()} disabled={!$undoEnabled}>Undo</button>
75
+ <button onclick={() => history.redo()} disabled={!$redoEnabled}>Redo</button>
71
76
  </div>
72
- {#if $activeElement != null}
73
- <hr />
74
- {#key $activeElement}
75
- <Editor element={$activeElement} />
76
- {/key}
77
- {/if}
78
- </div>
79
- <div class="footer">
80
- <a href="https://github.com/versatiles-org/node-versatiles-svelte/issues" target="_blank">
81
- Add a GitHub issue.</a
82
- >
77
+ <SidebarPanel title="Share map" open={false}>
78
+ <div class="flex flex-one">
79
+ <button value="Link" onclick={copyLink}>Link</button>
80
+ <button onclick={copyEmbedCode}>Embed Code</button>
81
+ </div>
82
+ </SidebarPanel>
83
+ <SidebarPanel title="Import/Export" open={false}>
84
+ <div class="flex flex-one">
85
+ <button onclick={importGeoJSON}>Import GeoJSON</button>
86
+ <button onclick={exportGeoJSON}>Export GeoJSON</button>
87
+ </div>
88
+ </SidebarPanel>
89
+ <SidebarPanel title="Add new">
90
+ <div class="flex flex-two">
91
+ <button onclick={() => activeElement.set(geometryManager.addNewMarker())}>Marker</button>
92
+ <button onclick={() => activeElement.set(geometryManager.addNewLine())}>Line</button>
93
+ <button onclick={() => activeElement.set(geometryManager.addNewPolygon())}>Polygon</button>
94
+ <button disabled>Circle</button>
95
+ </div>
96
+ </SidebarPanel>
97
+ <Editor element={$activeElement} />
98
+ <SidebarPanel title="Actions" disabled={!$activeElement}>
99
+ <div class="flex flex-two">
100
+ <button onclick={() => $activeElement!.delete()}>Delete</button>
101
+ </div>
102
+ </SidebarPanel>
103
+ <SidebarPanel title="Help">
104
+ <a
105
+ id="github_link"
106
+ href="https://github.com/versatiles-org/node-versatiles-svelte/issues"
107
+ target="_blank"
108
+ aria-label="Repository on GitHub">Report Bugs and Feature Requests as GitHub Issues</a
109
+ >
110
+ </SidebarPanel>
83
111
  </div>
84
112
  </div>
85
113
 
86
114
  <style>
87
115
  .sidebar {
88
- width: 200px;
116
+ --color-btn: #336680;
117
+ --color-bg: #ffffff;
118
+ --color-text: #000000;
119
+ --gap: 10px;
120
+
121
+ background-color: rgb(from var(--color-bg) r g b/ 0.7);
122
+ backdrop-filter: blur(10px);
123
+ box-sizing: border-box;
124
+ color: var(--color-text);
125
+ font-size: 0.8em;
89
126
  height: 100%;
127
+ overflow-y: scroll;
128
+ padding: var(--gap);
90
129
  position: absolute;
91
- top: 0;
92
130
  right: 0;
93
- background-color: #eee;
94
- overflow-y: scroll;
95
- box-sizing: border-box;
96
- padding: 0.5em var(--gap) 0;
97
- border-left: 0.5px solid rgba(0, 0, 0, 0.5);
98
-
99
- hr {
100
- border: none;
101
- border-top: 0.5px solid rgba(0, 0, 0, 1);
102
- margin: var(--gap) 0 var(--gap);
103
- }
104
-
105
- :global(h2) {
106
- font-size: 0.9em;
107
- font-weight: normal;
108
- opacity: 0.5;
109
- padding-top: var(--gap);
110
- border-top: 0.5px solid rgba(0, 0, 0, 1);
111
- margin: var(--gap) 0 var(--gap);
112
- text-align: center;
113
- }
131
+ top: 0;
132
+ width: 200px;
114
133
 
115
- :global(.row-flex) {
116
- margin-bottom: var(--gap);
134
+ .flex {
135
+ --gap: 5px;
136
+ align-items: center;
117
137
  display: flex;
138
+ flex-wrap: wrap;
139
+ gap: var(--gap);
118
140
  justify-content: space-between;
119
- column-gap: var(--gap);
120
- :global(input) {
121
- flex-grow: 0;
122
- }
123
- }
124
-
125
- :global(.row-input) {
126
141
  margin: var(--gap) 0 var(--gap);
127
- display: flex;
128
- justify-content: space-between;
129
- align-items: center;
130
- & > :global(label) {
131
- flex-grow: 0;
132
- }
133
- & > :global(button),
134
- & > :global(input),
135
- & > :global(select) {
136
- width: 60%;
137
- flex-grow: 0;
138
- }
139
- & > :global(input[type='checkbox']) {
140
- width: auto;
141
- }
142
+ width: 100%;
142
143
  }
143
-
144
- :global(label),
145
- :global(.label) {
146
- opacity: 0.7;
147
- font-size: 0.8em;
144
+ .flex-two button {
145
+ flex-grow: 1;
146
+ flex-basis: 0;
147
+ width: 40%;
148
148
  }
149
149
 
150
- :global(button),
151
- :global(input),
152
- :global(select) {
150
+ .flex-one button {
153
151
  width: 100%;
154
- box-sizing: border-box;
155
- margin: 0;
156
152
  }
157
153
 
158
- :global(p) {
159
- font-size: 0.8em;
160
- opacity: 0.5;
161
- margin: 0.5em 0 1em;
154
+ button {
155
+ background-color: var(--color-btn);
156
+ border: 2px solid var(--color-btn);
157
+ border-radius: 0.2em;
158
+ color: var(--color-bg);
159
+ cursor: pointer;
160
+ font-weight: 600;
161
+ padding: 0.4em 1em;
162
+ transition:
163
+ background-color 0.1s ease-in-out,
164
+ color 0.1s ease-in-out;
165
+
166
+ &:not([disabled]):hover {
167
+ background-color: var(--color-bg);
168
+ color: var(--color-btn);
169
+ }
170
+ &:disabled {
171
+ cursor: not-allowed;
172
+ opacity: 0.5;
173
+ }
162
174
  }
175
+ }
163
176
 
164
- .footer {
165
- text-align: right;
166
- font-size: 0.8em;
167
- a {
168
- color: #000;
169
- text-decoration: none;
170
- opacity: 0.3;
171
- &:hover {
172
- opacity: 1;
173
- }
174
- }
177
+ a {
178
+ text-decoration: none;
179
+ color: var(--fg-color);
180
+ &:hover {
181
+ text-decoration: underline;
175
182
  }
176
183
  }
177
184
  </style>