@grantcodes/ui 2.6.0 → 2.7.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 (44) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/custom-elements.json +556 -0
  3. package/package.json +4 -4
  4. package/src/components/accordion/accordion.component.js +4 -1
  5. package/src/components/accordion/accordion.css +26 -18
  6. package/src/components/code-preview/code-preview.css +5 -0
  7. package/src/components/container/container.css +6 -0
  8. package/src/components/countdown/countdown.component.js +180 -0
  9. package/src/components/countdown/countdown.css +62 -0
  10. package/src/components/countdown/countdown.js +6 -0
  11. package/src/components/countdown/countdown.react.js +9 -0
  12. package/src/components/countdown/countdown.stories.js +65 -0
  13. package/src/components/countdown/index.js +1 -0
  14. package/src/components/cta/cta.css +6 -0
  15. package/src/components/dialog/dialog.css +5 -0
  16. package/src/components/feature-list/feature-list.css +6 -0
  17. package/src/components/footer/footer.css +3 -1
  18. package/src/components/form-field/form-field.css +6 -0
  19. package/src/components/gallery/gallery.css +5 -0
  20. package/src/components/hero/hero.component.js +7 -0
  21. package/src/components/hero/hero.css +18 -1
  22. package/src/components/hero/hero.stories.js +30 -0
  23. package/src/components/icon/icon.css +6 -0
  24. package/src/components/loading/loading.css +5 -0
  25. package/src/components/logo-cloud/logo-cloud.css +6 -0
  26. package/src/components/map/index.js +1 -0
  27. package/src/components/map/map.component.js +135 -0
  28. package/src/components/map/map.css +41 -0
  29. package/src/components/map/map.js +6 -0
  30. package/src/components/map/map.react.js +9 -0
  31. package/src/components/map/map.stories.js +68 -0
  32. package/src/components/media-text/media-text.css +6 -0
  33. package/src/components/newsletter/newsletter.css +6 -0
  34. package/src/components/notice/notice.css +5 -0
  35. package/src/components/pagination/pagination.css +5 -0
  36. package/src/components/pricing/pricing.css +6 -0
  37. package/src/components/stats/stats.css +6 -0
  38. package/src/components/testimonials/testimonials.css +6 -0
  39. package/src/components/tooltip/tooltip.css +5 -0
  40. package/src/lib/styles/all.css +2 -0
  41. package/src/main.js +2 -0
  42. package/src/pages/blog-post.stories.js +7 -19
  43. package/src/react.js +2 -0
  44. package/src/types.d.ts +18 -0
@@ -0,0 +1,135 @@
1
+ import { LitElement, html, nothing } from "lit";
2
+ import mapStyles from "./map.css" with { type: "css" };
3
+
4
+ const DARK_FILTER = "invert(1) hue-rotate(180deg) brightness(0.95) contrast(0.9)";
5
+
6
+ export class GrantCodesMap extends LitElement {
7
+ static styles = [mapStyles];
8
+
9
+ static properties = {
10
+ /**
11
+ * Latitude coordinate.
12
+ * @type {string}
13
+ */
14
+ lat: { type: String },
15
+ /**
16
+ * Longitude coordinate.
17
+ * @type {string}
18
+ */
19
+ lng: { type: String },
20
+ /**
21
+ * Map zoom level (1-18).
22
+ * @type {number}
23
+ */
24
+ zoom: { type: Number },
25
+ /**
26
+ * Accessible label for the map iframe.
27
+ * @type {string}
28
+ */
29
+ label: { type: String },
30
+ /**
31
+ * URL for a "Get directions" link below the map.
32
+ * @type {string}
33
+ */
34
+ "directions-url": { type: String, attribute: "directions-url" },
35
+ /**
36
+ * Height of the map. Accepts any CSS length value.
37
+ * @type {string}
38
+ */
39
+ height: { type: String },
40
+ };
41
+
42
+ constructor() {
43
+ super();
44
+ this.lat = "";
45
+ this.lng = "";
46
+ this.zoom = 14;
47
+ this.label = "Map";
48
+ this["directions-url"] = "";
49
+ this.height = "";
50
+ this._darkQuery = window.matchMedia("(prefers-color-scheme: dark)");
51
+ this._onSchemeChange = () => this._updateFilter();
52
+ }
53
+
54
+ connectedCallback() {
55
+ super.connectedCallback();
56
+ this._observer = new MutationObserver(() => this._updateFilter());
57
+ this._observer.observe(document.documentElement, {
58
+ attributes: true,
59
+ attributeFilter: ["class"],
60
+ });
61
+ this._darkQuery.addEventListener("change", this._onSchemeChange);
62
+ this._updateFilter();
63
+ }
64
+
65
+ disconnectedCallback() {
66
+ super.disconnectedCallback();
67
+ this._observer?.disconnect();
68
+ this._darkQuery.removeEventListener("change", this._onSchemeChange);
69
+ }
70
+
71
+ _isDark() {
72
+ if (document.documentElement.classList.contains("dark")) return true;
73
+ if (document.documentElement.classList.contains("light")) return false;
74
+ return this._darkQuery.matches;
75
+ }
76
+
77
+ _updateFilter() {
78
+ this.style.setProperty(
79
+ "--g-map-filter",
80
+ this._isDark() ? DARK_FILTER : "none",
81
+ );
82
+ }
83
+
84
+ get _embedUrl() {
85
+ if (!this.lat || !this.lng) return "";
86
+ const bbox = this._getBoundingBox(
87
+ Number.parseFloat(this.lat),
88
+ Number.parseFloat(this.lng),
89
+ this.zoom,
90
+ );
91
+ return `https://www.openstreetmap.org/export/embed.html?bbox=${bbox}&marker=${this.lat},${this.lng}&layers=mapnik`;
92
+ }
93
+
94
+ /**
95
+ * Calculate a bounding box around a point for the given zoom level.
96
+ */
97
+ _getBoundingBox(lat, lng, zoom) {
98
+ const spread = 360 / 2 ** zoom;
99
+ return `${lng - spread},${lat - spread},${lng + spread},${lat + spread}`;
100
+ }
101
+
102
+ render() {
103
+ if (!this.lat || !this.lng) {
104
+ return nothing;
105
+ }
106
+
107
+ const heightStyle = this.height
108
+ ? `--map-height: ${this.height}`
109
+ : "";
110
+
111
+ return html`
112
+ <div class="map" style="${heightStyle}">
113
+ <iframe
114
+ class="map__iframe"
115
+ src="${this._embedUrl}"
116
+ title="${this.label}"
117
+ loading="lazy"
118
+ referrerpolicy="no-referrer"
119
+ frameborder="0"
120
+ seamless
121
+ ></iframe>
122
+ ${
123
+ this["directions-url"]
124
+ ? html`<a
125
+ class="map__directions"
126
+ href="${this["directions-url"]}"
127
+ target="_blank"
128
+ rel="noopener noreferrer"
129
+ >Get directions</a>`
130
+ : nothing
131
+ }
132
+ </div>
133
+ `;
134
+ }
135
+ }
@@ -0,0 +1,41 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ :host {
8
+ display: block;
9
+ }
10
+
11
+ .map {
12
+ position: relative;
13
+ border: 1px solid var(--g-theme-color-border-default);
14
+ border-radius: var(--g-theme-border-radius-md, 0.5rem);
15
+ overflow: hidden;
16
+ }
17
+
18
+ .map__iframe {
19
+ display: block;
20
+ vertical-align: top;
21
+ width: 100%;
22
+ height: var(--map-height, 21rem);
23
+ border: 0;
24
+ filter: var(--g-map-filter, none);
25
+ }
26
+
27
+ .map__directions {
28
+ display: block;
29
+ padding: var(--g-theme-spacing-sm) var(--g-theme-spacing-md);
30
+ font-size: var(--g-theme-typography-body-sm-font-size);
31
+ color: var(--g-theme-color-content-brand, #7c3aed);
32
+ text-decoration: none;
33
+ border-block-start: 1px solid var(--g-theme-color-border-default);
34
+ background: var(--g-theme-color-background-subtle);
35
+ transition: background-color 0.2s ease;
36
+ }
37
+
38
+ .map__directions:hover {
39
+ background: var(--g-theme-color-background-subtle-hover);
40
+ text-decoration: underline;
41
+ }
@@ -0,0 +1,6 @@
1
+ import { GrantCodesMap } from "./map.component.js";
2
+
3
+ export * from "./map.component.js";
4
+ export default GrantCodesMap;
5
+
6
+ customElements.define("grantcodes-map", GrantCodesMap);
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import { createComponent } from "@lit/react";
3
+ import { GrantCodesMap } from "./map.js";
4
+
5
+ export const MapEmbed = createComponent({
6
+ tagName: "grantcodes-map",
7
+ elementClass: GrantCodesMap,
8
+ react: React,
9
+ });
@@ -0,0 +1,68 @@
1
+ import { getStorybookHelpers } from "@wc-toolkit/storybook-helpers";
2
+ import "./map.js";
3
+
4
+ const { events, args, argTypes } = getStorybookHelpers("grantcodes-map");
5
+
6
+ const meta = {
7
+ title: "Components/Map",
8
+ component: "grantcodes-map",
9
+ args,
10
+ argTypes,
11
+ parameters: {
12
+ actions: {
13
+ handles: events,
14
+ },
15
+ },
16
+ };
17
+
18
+ export default meta;
19
+
20
+ /**
21
+ * Default map showing a location in Granollers, Catalonia.
22
+ */
23
+ export const Default = {
24
+ args: {
25
+ lat: "41.648747",
26
+ lng: "2.161975",
27
+ zoom: 14,
28
+ label: "Granollers, Catalonia",
29
+ },
30
+ };
31
+
32
+ /**
33
+ * Map with a directions link below.
34
+ */
35
+ export const WithDirections = {
36
+ args: {
37
+ lat: "41.648747",
38
+ lng: "2.161975",
39
+ zoom: 14,
40
+ label: "Granollers, Catalonia",
41
+ "directions-url": "https://maps.google.com/?q=41.648747,2.161975",
42
+ },
43
+ };
44
+
45
+ /**
46
+ * Map with a custom height.
47
+ */
48
+ export const CustomHeight = {
49
+ args: {
50
+ lat: "48.8566",
51
+ lng: "2.3522",
52
+ zoom: 12,
53
+ label: "Paris, France",
54
+ height: "600px",
55
+ },
56
+ };
57
+
58
+ /**
59
+ * Zoomed-out world view.
60
+ */
61
+ export const ZoomedOut = {
62
+ args: {
63
+ lat: "0",
64
+ lng: "0",
65
+ zoom: 2,
66
+ label: "World map",
67
+ },
68
+ };
@@ -1,3 +1,9 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ }
6
+
1
7
  :host {
2
8
  display: block;
3
9
  }
@@ -1,3 +1,9 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ }
6
+
1
7
  :host {
2
8
  display: block;
3
9
  }
@@ -1,3 +1,8 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ }
1
6
 
2
7
  .notice {
3
8
  display: flex;
@@ -1,3 +1,8 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ }
1
6
 
2
7
  .pagination {
3
8
  display: flex;
@@ -1,3 +1,9 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ }
6
+
1
7
  :host {
2
8
  display: block;
3
9
  }
@@ -1,3 +1,9 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ }
6
+
1
7
  :host {
2
8
  display: block;
3
9
  }
@@ -1,3 +1,9 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ }
6
+
1
7
  :host {
2
8
  display: block;
3
9
  }
@@ -1,3 +1,8 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ }
1
6
 
2
7
  :host,
3
8
  .tooltip,
@@ -10,6 +10,7 @@
10
10
  @import "../../components/card/card.css";
11
11
  @import "../../components/code-preview/code-preview.css";
12
12
  @import "../../components/container/container.css";
13
+ @import "../../components/countdown/countdown.css";
13
14
  @import "../../components/cta/cta.css";
14
15
  @import "../../components/dialog/dialog.css";
15
16
  @import "../../components/dropdown/dropdown.css";
@@ -23,6 +24,7 @@
23
24
  @import "../../components/icon/icon.css";
24
25
  @import "../../components/loading/loading.css";
25
26
  @import "../../components/logo-cloud/logo-cloud.css";
27
+ @import "../../components/map/map.css";
26
28
  @import "../../components/media-text/media-text.css";
27
29
  @import "../../components/newsletter/newsletter.css";
28
30
  @import "../../components/notice/notice.css";
package/src/main.js CHANGED
@@ -5,6 +5,7 @@ export * from "./components/button-group/index.js";
5
5
  export * from "./components/card/index.js";
6
6
  export * from "./components/code-preview/index.js";
7
7
  export * from "./components/container/index.js";
8
+ export * from "./components/countdown/index.js";
8
9
  export * from "./components/cta/index.js";
9
10
  export * from "./components/dialog/index.js";
10
11
  export * from "./components/dropzone/index.js";
@@ -15,6 +16,7 @@ export * from "./components/hero/index.js";
15
16
  export * from "./components/icon/index.js";
16
17
  export * from "./components/loading/index.js";
17
18
  export * from "./components/logo-cloud/index.js";
19
+ export * from "./components/map/index.js";
18
20
  export * from "./components/media-text/index.js";
19
21
  export * from "./components/newsletter/index.js";
20
22
  export * from "./components/notice/index.js";
@@ -1,5 +1,6 @@
1
1
  import { html } from "lit";
2
2
  import "../components/app-bar/app-bar.js";
3
+ import "../components/app-bar/nav-link.js";
3
4
  import "../components/button/button.js";
4
5
  import "../components/breadcrumb/breadcrumb.js";
5
6
  import "../components/container/container.js";
@@ -93,10 +94,10 @@ export const Default = {
93
94
  Flowbase
94
95
  </a>
95
96
  <div slot="nav" style="display: flex; gap: 0.5rem;">
96
- <a href="/">Home</a>
97
- <a href="/features">Features</a>
98
- <a href="/blog">Blog</a>
99
- <a href="/pricing">Pricing</a>
97
+ <grantcodes-nav-link><a href="/">Home</a></grantcodes-nav-link>
98
+ <grantcodes-nav-link><a href="/features">Features</a></grantcodes-nav-link>
99
+ <grantcodes-nav-link><a href="/blog">Blog</a></grantcodes-nav-link>
100
+ <grantcodes-nav-link><a href="/pricing">Pricing</a></grantcodes-nav-link>
100
101
  </div>
101
102
  <div slot="actions" style="display: flex; gap: 0.5rem;">
102
103
  <grantcodes-button variant="ghost">Sign in</grantcodes-button>
@@ -287,7 +288,7 @@ export const Default = {
287
288
 
288
289
  <!-- Related posts -->
289
290
  <div
290
- style="background: var(--g-theme-color-background-subtle); padding-block: var(--g-theme-spacing-2xl);"
291
+ style="padding-block: var(--g-theme-spacing-2xl);"
291
292
  >
292
293
  <grantcodes-container>
293
294
  <h2
@@ -352,20 +353,7 @@ export const Default = {
352
353
  </grantcodes-container>
353
354
  </div>
354
355
 
355
- <grantcodes-cta
356
- eyebrow="Ready to try it?"
357
- title="See how Flowbase keeps your team in flow"
358
- text="Join thousands of teams who've replaced their meeting-heavy workflows with a calmer, more productive way of working."
359
- primary-action=${JSON.stringify({
360
- label: "Start for free",
361
- href: "/signup",
362
- })}
363
- secondary-action=${JSON.stringify({
364
- label: "See all features",
365
- href: "/features",
366
- })}
367
- align="center"
368
- ></grantcodes-cta>
356
+
369
357
 
370
358
  <grantcodes-footer columns="3">
371
359
  ${footerContent}
package/src/react.js CHANGED
@@ -9,6 +9,7 @@ export { ButtonGroup } from "./components/button-group/button-group.react.js"
9
9
  export { Card } from "./components/card/card.react.js"
10
10
  export { CodePreview } from "./components/code-preview/code-preview.react.js"
11
11
  export { Container } from "./components/container/container.react.js"
12
+ export { Countdown } from "./components/countdown/countdown.react.js"
12
13
  export { Cta } from "./components/cta/cta.react.js"
13
14
  export { Dialog } from "./components/dialog/dialog.react.js"
14
15
  export { Dropdown, DropdownItem } from "./components/dropdown/dropdown.react.js"
@@ -22,6 +23,7 @@ export { Hero } from "./components/hero/hero.react.js"
22
23
  export { Icon } from "./components/icon/icon.react.js"
23
24
  export { Loading } from "./components/loading/loading.react.js"
24
25
  export { LogoCloud } from "./components/logo-cloud/logo-cloud.react.js"
26
+ export { MapEmbed } from "./components/map/map.react.js"
25
27
  export { MediaText } from "./components/media-text/media-text.react.js"
26
28
  export { Newsletter } from "./components/newsletter/newsletter.react.js"
27
29
  export { Notice } from "./components/notice/notice.react.js"
package/src/types.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Ambient module declarations for @grantcodes/ui.
3
+ *
4
+ * Usage: Add to your tsconfig.json:
5
+ * { "compilerOptions": { "types": ["@grantcodes/ui"] } }
6
+ *
7
+ * Or reference directly:
8
+ * /// <reference types="@grantcodes/ui" />
9
+ */
10
+
11
+ // Component deep imports
12
+ declare module "@grantcodes/ui/components/*";
13
+
14
+ // Style imports
15
+ declare module "@grantcodes/ui/styles/*";
16
+
17
+ // Font imports
18
+ declare module "@grantcodes/ui/fonts/*";