@newschools/sdk 0.2.4 → 0.2.5
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.
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Nuxt module for integrating New Schools learning content into your applications
|
|
|
8
8
|
🎨 **Automatic theming** - Organization brand colors applied as CSS variables
|
|
9
9
|
🔒 **Type-safe** - Full TypeScript support with entity types
|
|
10
10
|
⚡ **SSR-compatible** - Built on Nuxt's `useAsyncData`
|
|
11
|
-
🎯 **Ready-to-use components** -
|
|
11
|
+
🎯 **Ready-to-use components** - EntityHero, EntityBentoGrid, EntityCard
|
|
12
12
|
📐 **Smart layouts** - EntityBentoGrid automatically generates responsive layouts
|
|
13
13
|
🔑 **API key authentication** - Secure access to your organization's content
|
|
14
14
|
|
|
@@ -139,13 +139,15 @@ const organization = useOrganization();
|
|
|
139
139
|
|
|
140
140
|
### Pre-built Components
|
|
141
141
|
|
|
142
|
-
####
|
|
142
|
+
#### EntityHero
|
|
143
143
|
|
|
144
|
-
Display
|
|
144
|
+
Display entity header with cover image and branding. Supports 4 layout variants:
|
|
145
|
+
|
|
146
|
+
**Basic Usage:**
|
|
145
147
|
|
|
146
148
|
```vue
|
|
147
149
|
<template>
|
|
148
|
-
<
|
|
150
|
+
<EntityHero :org="organization" variant="left">
|
|
149
151
|
<template #sub-title>
|
|
150
152
|
<p>Welcome to our learning platform</p>
|
|
151
153
|
</template>
|
|
@@ -153,7 +155,7 @@ Display organization header with cover image and branding:
|
|
|
153
155
|
<template #content>
|
|
154
156
|
<div>Custom content area for CTAs, forms, etc.</div>
|
|
155
157
|
</template>
|
|
156
|
-
</
|
|
158
|
+
</EntityHero>
|
|
157
159
|
</template>
|
|
158
160
|
|
|
159
161
|
<script setup lang="ts">
|
|
@@ -161,6 +163,48 @@ const organization = useOrganization();
|
|
|
161
163
|
</script>
|
|
162
164
|
```
|
|
163
165
|
|
|
166
|
+
**Layout Variants:**
|
|
167
|
+
|
|
168
|
+
- `variant="left"` (default) - Image on left, content on right (grid on tablet+, stacked on mobile)
|
|
169
|
+
- `variant="right"` - Content on left, image on right (grid on tablet+, stacked on mobile)
|
|
170
|
+
- `variant="fullscreen"` - Full screen image with content overlay at bottom left (all screen sizes)
|
|
171
|
+
- `variant="horizontal"` - Image on top (50vh with clamp), content below (stacked on all sizes)
|
|
172
|
+
|
|
173
|
+
**Variant Examples:**
|
|
174
|
+
|
|
175
|
+
```vue
|
|
176
|
+
<!-- Fullscreen: Content overlays image -->
|
|
177
|
+
<EntityHero :org="organization" variant="fullscreen">
|
|
178
|
+
<template #sub-title>Journey subtitle</template>
|
|
179
|
+
<template #content>
|
|
180
|
+
<p>This content appears over the image, under the title.</p>
|
|
181
|
+
</template>
|
|
182
|
+
</EntityHero>
|
|
183
|
+
|
|
184
|
+
<!-- Horizontal: Image top, content bottom -->
|
|
185
|
+
<EntityHero :org="organization" variant="horizontal">
|
|
186
|
+
<template #sub-title>Course overview</template>
|
|
187
|
+
<template #content>
|
|
188
|
+
<div>Course details and enrollment form</div>
|
|
189
|
+
</template>
|
|
190
|
+
</EntityHero>
|
|
191
|
+
|
|
192
|
+
<!-- Right: Reversed grid layout -->
|
|
193
|
+
<EntityHero :org="organization" variant="right">
|
|
194
|
+
<template #sub-title>Featured content</template>
|
|
195
|
+
<template #content>
|
|
196
|
+
<button>Get Started</button>
|
|
197
|
+
</template>
|
|
198
|
+
</EntityHero>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Responsive Behavior:**
|
|
202
|
+
|
|
203
|
+
- Title and subtitle always overlay the image (all variants)
|
|
204
|
+
- `left` and `right` variants stack vertically on mobile (<768px)
|
|
205
|
+
- `fullscreen` maintains overlay layout on all screen sizes
|
|
206
|
+
- `horizontal` uses flexible image height: `clamp(40vh, 50vh, 60vh)`
|
|
207
|
+
|
|
164
208
|
#### EntityBentoGrid (Recommended)
|
|
165
209
|
|
|
166
210
|
Display entities in a responsive bento grid with automatic layout:
|
|
@@ -452,19 +496,26 @@ const journey = await client.get<Journey>("/journeys/my-slug");
|
|
|
452
496
|
|
|
453
497
|
## Components Reference
|
|
454
498
|
|
|
455
|
-
### `<
|
|
499
|
+
### `<EntityHero>`
|
|
456
500
|
|
|
457
|
-
Display
|
|
501
|
+
Display entity header with cover image and branding
|
|
458
502
|
|
|
459
503
|
**Props:**
|
|
460
504
|
|
|
461
505
|
- `org: Organisation` - Organization object (required)
|
|
506
|
+
- `variant?: 'left' | 'right' | 'fullscreen' | 'horizontal'` - Layout variant (default: 'left')
|
|
462
507
|
|
|
463
508
|
**Slots:**
|
|
464
509
|
|
|
465
|
-
- `sub-title` - Content below
|
|
466
|
-
- `content` -
|
|
467
|
-
|
|
510
|
+
- `sub-title` - Content below entity name (overlays image)
|
|
511
|
+
- `content` - Main content area (position varies by variant)
|
|
512
|
+
|
|
513
|
+
**Layout Behavior:**
|
|
514
|
+
|
|
515
|
+
- `left`: Grid layout (image left, content right) on tablet+, stacks on mobile
|
|
516
|
+
- `right`: Grid layout (content left, image right) on tablet+, stacks on mobile
|
|
517
|
+
- `fullscreen`: Content overlays image at bottom left (all screen sizes)
|
|
518
|
+
- `horizontal`: Image top with flexible height, content below (all screen sizes)
|
|
468
519
|
|
|
469
520
|
### `<EntityBentoGrid>`
|
|
470
521
|
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<component
|
|
3
|
+
:is="isLink ? NuxtLinkEl : motion.div"
|
|
4
|
+
v-bind="rootAttrs"
|
|
3
5
|
class="ns-entity-card"
|
|
4
|
-
:initial="{ opacity: 0,
|
|
5
|
-
:
|
|
6
|
-
:transition="{ duration: 0.
|
|
6
|
+
:initial="{ opacity: 0, scale: 0.95 }"
|
|
7
|
+
:whileInView="{ opacity: 1, scale: 1 }"
|
|
8
|
+
:transition="{ duration: 0.8, delay: 0.2, ease: [0.19, 1, 0.22, 1] }"
|
|
7
9
|
>
|
|
8
10
|
<!-- Image Container -->
|
|
9
11
|
<div class="ns-entity-card__image">
|
|
10
|
-
<
|
|
12
|
+
<EntityImage
|
|
13
|
+
v-if="imageUrl"
|
|
14
|
+
:src="imageUrl"
|
|
15
|
+
:alt="imageAlt || 'Card image'"
|
|
16
|
+
/>
|
|
11
17
|
|
|
12
|
-
|
|
13
|
-
<EntityImage :src="imageUrl" :alt="imageAlt || 'Card image'" />
|
|
18
|
+
<FlickeringGridBackground v-else :max-opacity="0.05" color="#FFFFFF" />
|
|
14
19
|
</div>
|
|
15
20
|
|
|
16
21
|
<!-- Content overlay at bottom -->
|
|
@@ -22,7 +27,7 @@
|
|
|
22
27
|
<slot name="description" />
|
|
23
28
|
</p>
|
|
24
29
|
</div>
|
|
25
|
-
</
|
|
30
|
+
</component>
|
|
26
31
|
</template>
|
|
27
32
|
|
|
28
33
|
<script setup lang="ts">
|
|
@@ -36,9 +41,18 @@ interface Props {
|
|
|
36
41
|
|
|
37
42
|
/** Image alt text */
|
|
38
43
|
imageAlt?: string;
|
|
44
|
+
|
|
45
|
+
/** Navigation link (optional - makes card a NuxtLink) */
|
|
46
|
+
to?: string;
|
|
39
47
|
}
|
|
40
48
|
|
|
41
|
-
defineProps<Props>();
|
|
49
|
+
const props = defineProps<Props>();
|
|
50
|
+
|
|
51
|
+
const NuxtLinkEl = resolveComponent("NuxtLink");
|
|
52
|
+
const isLink = computed(() => !!props.to);
|
|
53
|
+
|
|
54
|
+
// Only pass valid attributes to the rendered root
|
|
55
|
+
const rootAttrs = computed(() => (isLink.value ? { to: props.to } : {}));
|
|
42
56
|
</script>
|
|
43
57
|
|
|
44
58
|
<style scoped>
|
|
@@ -49,9 +63,8 @@ defineProps<Props>();
|
|
|
49
63
|
*/
|
|
50
64
|
|
|
51
65
|
.ns-entity-card {
|
|
52
|
-
|
|
66
|
+
position: relative;
|
|
53
67
|
background: rgba(255, 255, 255, 0.08);
|
|
54
|
-
backdrop-filter: blur(20px);
|
|
55
68
|
border-radius: var(--ns-border-radius-md);
|
|
56
69
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
57
70
|
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
|
@@ -67,7 +80,6 @@ defineProps<Props>();
|
|
|
67
80
|
cursor: pointer;
|
|
68
81
|
}
|
|
69
82
|
|
|
70
|
-
/* Hover effect */
|
|
71
83
|
.ns-entity-card:hover {
|
|
72
84
|
background: rgba(255, 255, 255, 0.12);
|
|
73
85
|
border-color: rgba(255, 255, 255, 0.25);
|
|
@@ -75,7 +87,6 @@ defineProps<Props>();
|
|
|
75
87
|
transform: translateY(-4px);
|
|
76
88
|
}
|
|
77
89
|
|
|
78
|
-
/* Active state */
|
|
79
90
|
.ns-entity-card:active {
|
|
80
91
|
transform: translateY(-2px);
|
|
81
92
|
}
|
|
@@ -88,7 +99,8 @@ defineProps<Props>();
|
|
|
88
99
|
position: relative;
|
|
89
100
|
width: 100%;
|
|
90
101
|
height: 100%; /* Fill parent height (works for bento grid cells) */
|
|
91
|
-
border-radius: var(--ns-border-radius-
|
|
102
|
+
border-radius: var(--ns-border-radius-sm);
|
|
103
|
+
overflow: hidden;
|
|
92
104
|
|
|
93
105
|
/* Layout */
|
|
94
106
|
display: flex;
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- Glass Container with Layout Variants -->
|
|
3
|
+
<motion.div
|
|
4
|
+
class="ns-entity-hero__glass-container"
|
|
5
|
+
:class="`ns-entity-hero--${variant}`"
|
|
6
|
+
:initial="{ opacity: 0, scale: 0.95 }"
|
|
7
|
+
:animate="{ opacity: 1, scale: 1 }"
|
|
8
|
+
:transition="{ duration: 0.8, delay: 0.2, ease: [0.19, 1, 0.22, 1] }"
|
|
9
|
+
>
|
|
10
|
+
<!-- Image Container -->
|
|
11
|
+
<div class="ns-entity-hero__image">
|
|
12
|
+
<!-- Cover image if available -->
|
|
13
|
+
<EntityImage
|
|
14
|
+
v-if="org.coverImageUrl"
|
|
15
|
+
:src="org.coverImageUrl"
|
|
16
|
+
:alt="`${org.name} cover image`"
|
|
17
|
+
/>
|
|
18
|
+
|
|
19
|
+
<FlickeringGridBackground v-else :max-opacity="0.05" color="#FFFFFF" />
|
|
20
|
+
|
|
21
|
+
<!-- Title and subtitle overlay (always over image) -->
|
|
22
|
+
<div class="ns-entity-hero__image-content">
|
|
23
|
+
<h1 class="ns-entity-hero__image-title">{{ org.name }}</h1>
|
|
24
|
+
|
|
25
|
+
<p class="ns-entity-hero__sub-title" v-if="$slots['sub-title']">
|
|
26
|
+
<slot name="sub-title" />
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
<!-- Content slot for fullscreen variant (under title) -->
|
|
30
|
+
<div
|
|
31
|
+
v-if="variant === 'fullscreen' && $slots['content']"
|
|
32
|
+
class="ns-entity-hero__content-overlay"
|
|
33
|
+
>
|
|
34
|
+
<slot name="content" />
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<!-- Content Area (for non-fullscreen variants) -->
|
|
40
|
+
<div
|
|
41
|
+
v-if="variant !== 'fullscreen' && $slots['content']"
|
|
42
|
+
class="ns-entity-hero__content"
|
|
43
|
+
>
|
|
44
|
+
<slot name="content" />
|
|
45
|
+
</div>
|
|
46
|
+
</motion.div>
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<script setup lang="ts">
|
|
50
|
+
import { motion } from "motion-v";
|
|
51
|
+
import FlickeringGridBackground from "../internal/components/FlickeringGridBackground.vue";
|
|
52
|
+
import EntityImage from "./EntityImage.vue";
|
|
53
|
+
import type { Organisation } from "../../types/entities";
|
|
54
|
+
|
|
55
|
+
interface Props {
|
|
56
|
+
/** Organization object */
|
|
57
|
+
org: Organisation;
|
|
58
|
+
|
|
59
|
+
/** Layout variant */
|
|
60
|
+
variant?: "left" | "right" | "fullscreen" | "horizontal";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
withDefaults(defineProps<Props>(), {
|
|
64
|
+
variant: "left",
|
|
65
|
+
});
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<style scoped>
|
|
69
|
+
/**
|
|
70
|
+
* Entity Hero Component Styles
|
|
71
|
+
* Glassy container with multiple layout variants
|
|
72
|
+
* Uses New Schools SDK tokens (--ns-*)
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
/* ============================================ */
|
|
76
|
+
/* GLASS CONTAINER - BASE */
|
|
77
|
+
/* ============================================ */
|
|
78
|
+
|
|
79
|
+
.ns-entity-hero__glass-container {
|
|
80
|
+
position: relative;
|
|
81
|
+
width: 100%;
|
|
82
|
+
background: rgba(255, 255, 255, 0.08);
|
|
83
|
+
border-radius: var(--ns-border-radius-md);
|
|
84
|
+
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
85
|
+
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
|
86
|
+
overflow: hidden;
|
|
87
|
+
|
|
88
|
+
/* Mobile first: flex column (stack) */
|
|
89
|
+
display: flex;
|
|
90
|
+
flex-direction: column;
|
|
91
|
+
gap: var(--ns-spacing-md);
|
|
92
|
+
padding: var(--ns-spacing-md);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* ============================================ */
|
|
96
|
+
/* VARIANT: FULLSCREEN */
|
|
97
|
+
/* ============================================ */
|
|
98
|
+
|
|
99
|
+
.ns-entity-hero--fullscreen {
|
|
100
|
+
height: calc(100dvh - var(--ns-spacing-2xl));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.ns-entity-hero--fullscreen .ns-entity-hero__image {
|
|
104
|
+
flex: 1;
|
|
105
|
+
height: 100%;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* ============================================ */
|
|
109
|
+
/* VARIANT: LEFT (Default) */
|
|
110
|
+
/* ============================================ */
|
|
111
|
+
|
|
112
|
+
/* Mobile: stacked (default flex-column) */
|
|
113
|
+
.ns-entity-hero--left {
|
|
114
|
+
height: calc(100dvh - var(--ns-spacing-2xl));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* Tablet+: Grid with image left */
|
|
118
|
+
@media (min-width: 768px) {
|
|
119
|
+
.ns-entity-hero--left {
|
|
120
|
+
display: grid;
|
|
121
|
+
grid-template-columns: 1.5fr 1fr;
|
|
122
|
+
gap: var(--ns-spacing-xl);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* ============================================ */
|
|
127
|
+
/* VARIANT: RIGHT */
|
|
128
|
+
/* ============================================ */
|
|
129
|
+
|
|
130
|
+
/* Mobile: stacked (default flex-column) */
|
|
131
|
+
.ns-entity-hero--right {
|
|
132
|
+
height: calc(100dvh - var(--ns-spacing-2xl));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* Tablet+: Grid with image right */
|
|
136
|
+
@media (min-width: 768px) {
|
|
137
|
+
.ns-entity-hero--right {
|
|
138
|
+
display: grid;
|
|
139
|
+
grid-template-columns: 1fr 1.5fr;
|
|
140
|
+
gap: var(--ns-spacing-xl);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.ns-entity-hero--right .ns-entity-hero__image {
|
|
144
|
+
order: 2;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.ns-entity-hero--right .ns-entity-hero__content {
|
|
148
|
+
order: 1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* ============================================ */
|
|
153
|
+
/* VARIANT: HORIZONTAL */
|
|
154
|
+
/* ============================================ */
|
|
155
|
+
|
|
156
|
+
.ns-entity-hero--horizontal {
|
|
157
|
+
min-height: calc(100dvh - var(--ns-spacing-2xl));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.ns-entity-hero--horizontal .ns-entity-hero__image {
|
|
161
|
+
height: clamp(40vh, 50vh, 60vh);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.ns-entity-hero--horizontal .ns-entity-hero__content {
|
|
165
|
+
flex: 1;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* ============================================ */
|
|
169
|
+
/* IMAGE CONTAINER */
|
|
170
|
+
/* ============================================ */
|
|
171
|
+
|
|
172
|
+
.ns-entity-hero__image {
|
|
173
|
+
border-radius: var(--ns-border-radius-md);
|
|
174
|
+
overflow: hidden;
|
|
175
|
+
position: relative;
|
|
176
|
+
min-height: 50vh;
|
|
177
|
+
|
|
178
|
+
background: linear-gradient(
|
|
179
|
+
180deg,
|
|
180
|
+
var(--ns-color-grey-900, #2d3748) 0%,
|
|
181
|
+
var(--ns-color-black, #1a202c) 100%
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
display: flex;
|
|
185
|
+
align-items: flex-end;
|
|
186
|
+
justify-content: flex-start;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* Tablet+: auto height for grid layouts */
|
|
190
|
+
@media (min-width: 768px) {
|
|
191
|
+
.ns-entity-hero--left .ns-entity-hero__image,
|
|
192
|
+
.ns-entity-hero--right .ns-entity-hero__image {
|
|
193
|
+
min-height: auto;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* ============================================ */
|
|
198
|
+
/* IMAGE CONTENT OVERLAY */
|
|
199
|
+
/* ============================================ */
|
|
200
|
+
|
|
201
|
+
.ns-entity-hero__image-content {
|
|
202
|
+
position: absolute;
|
|
203
|
+
bottom: 0;
|
|
204
|
+
left: 0;
|
|
205
|
+
right: 0;
|
|
206
|
+
padding: var(--ns-spacing-xl);
|
|
207
|
+
z-index: 3;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.ns-entity-hero__image-title {
|
|
211
|
+
margin: 0 0 var(--ns-spacing-sm) 0;
|
|
212
|
+
color: var(--ns-color-white);
|
|
213
|
+
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.ns-entity-hero__sub-title {
|
|
217
|
+
margin: 0 0 var(--ns-spacing-md) 0;
|
|
218
|
+
color: var(--ns-color-white);
|
|
219
|
+
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* Content overlay for fullscreen variant (under title/subtitle) */
|
|
223
|
+
.ns-entity-hero__content-overlay {
|
|
224
|
+
margin-top: var(--ns-spacing-lg);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/* ============================================ */
|
|
228
|
+
/* CONTENT AREA (Non-fullscreen variants) */
|
|
229
|
+
/* ============================================ */
|
|
230
|
+
|
|
231
|
+
.ns-entity-hero__content {
|
|
232
|
+
display: flex;
|
|
233
|
+
flex-direction: column;
|
|
234
|
+
padding: var(--ns-spacing-md);
|
|
235
|
+
}
|
|
236
|
+
</style>
|
|
@@ -37,8 +37,8 @@ interface Props {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const props = withDefaults(defineProps<Props>(), {
|
|
40
|
-
blurLayers:
|
|
41
|
-
blurIntensity: 0.
|
|
40
|
+
blurLayers: 4,
|
|
41
|
+
blurIntensity: 0.3,
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
const segmentSize = 1 / (props.blurLayers + 1);
|
|
@@ -70,18 +70,11 @@ const getBlurLayerStyle = (index: number) => {
|
|
|
70
70
|
*/
|
|
71
71
|
|
|
72
72
|
.ns-entity-image {
|
|
73
|
-
position: absolute;
|
|
74
|
-
top: 0;
|
|
75
|
-
left: 0;
|
|
76
73
|
width: 100%;
|
|
77
74
|
height: 100%;
|
|
78
|
-
z-index: 1;
|
|
79
75
|
}
|
|
80
76
|
|
|
81
77
|
.ns-entity-image__picture {
|
|
82
|
-
position: absolute;
|
|
83
|
-
top: 0;
|
|
84
|
-
left: 0;
|
|
85
78
|
width: 100%;
|
|
86
79
|
height: 100%;
|
|
87
80
|
}
|
package/package.json
CHANGED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<!-- Glass Container with Grid Layout -->
|
|
3
|
-
<motion.div
|
|
4
|
-
class="ns-org-hero__glass-container"
|
|
5
|
-
:initial="{ opacity: 0, scale: 0.95 }"
|
|
6
|
-
:animate="{ opacity: 1, scale: 1 }"
|
|
7
|
-
:transition="{ duration: 0.8, delay: 0.2, ease: [0.19, 1, 0.22, 1] }"
|
|
8
|
-
>
|
|
9
|
-
<!-- Image Container (Left) -->
|
|
10
|
-
<div class="ns-org-hero__image">
|
|
11
|
-
<FlickeringGridBackground :max-opacity="0.05" color="#FFFFFF" />
|
|
12
|
-
|
|
13
|
-
<!-- Cover image if available -->
|
|
14
|
-
<EntityImage :src="org.coverImageUrl" :alt="`${org.name} cover image`" />
|
|
15
|
-
|
|
16
|
-
<!-- Organisation name overlay -->
|
|
17
|
-
<div class="ns-org-hero__image-content">
|
|
18
|
-
<h1 class="ns-org-hero__image-title">{{ org.name }}</h1>
|
|
19
|
-
|
|
20
|
-
<p class="ns-org-hero__sub-title">
|
|
21
|
-
<slot name="sub-title" v-if="$slots['sub-title']" />
|
|
22
|
-
</p>
|
|
23
|
-
</div>
|
|
24
|
-
</div>
|
|
25
|
-
|
|
26
|
-
<!-- Content Area (Right) -->
|
|
27
|
-
<div class="ns-org-hero__content" v-if="$slots['content']">
|
|
28
|
-
<slot name="content" />
|
|
29
|
-
</div>
|
|
30
|
-
</motion.div>
|
|
31
|
-
</template>
|
|
32
|
-
|
|
33
|
-
<script setup lang="ts">
|
|
34
|
-
import { motion } from "motion-v";
|
|
35
|
-
import FlickeringGridBackground from "../internal/components/FlickeringGridBackground.vue";
|
|
36
|
-
import EntityImage from "./EntityImage.vue";
|
|
37
|
-
import type { Organisation } from "../../types/entities";
|
|
38
|
-
|
|
39
|
-
interface Props {
|
|
40
|
-
/** Organization object */
|
|
41
|
-
org: Organisation;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
defineProps<Props>();
|
|
45
|
-
</script>
|
|
46
|
-
|
|
47
|
-
<style scoped>
|
|
48
|
-
/**
|
|
49
|
-
* Organization Hero Component Styles
|
|
50
|
-
* Glassy container with grid layout
|
|
51
|
-
* Uses New Schools SDK tokens (--ns-*)
|
|
52
|
-
*/
|
|
53
|
-
|
|
54
|
-
/* ============================================ */
|
|
55
|
-
/* GLASS CONTAINER WITH GRID LAYOUT */
|
|
56
|
-
/* ============================================ */
|
|
57
|
-
|
|
58
|
-
.ns-org-hero__glass-container {
|
|
59
|
-
width: 100%;
|
|
60
|
-
height: calc(100dvh - var(--ns-spacing-2xl));
|
|
61
|
-
/* Glassmorphism effect */
|
|
62
|
-
background: rgba(255, 255, 255, 0.08);
|
|
63
|
-
backdrop-filter: blur(20px);
|
|
64
|
-
border-radius: var(--ns-border-radius-md);
|
|
65
|
-
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
66
|
-
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
|
67
|
-
|
|
68
|
-
/* Mobile first: flex column */
|
|
69
|
-
display: flex;
|
|
70
|
-
flex-direction: column;
|
|
71
|
-
gap: var(--ns-spacing-md);
|
|
72
|
-
padding: var(--ns-spacing-md);
|
|
73
|
-
overflow: hidden;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/* Tablet and up: Grid layout */
|
|
77
|
-
@media (min-width: 768px) {
|
|
78
|
-
.ns-org-hero__glass-container {
|
|
79
|
-
display: grid;
|
|
80
|
-
grid-template-columns: 1.5fr 1fr;
|
|
81
|
-
gap: var(--ns-spacing-xl);
|
|
82
|
-
padding: var(--ns-spacing-lg);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/* ============================================ */
|
|
87
|
-
/* IMAGE CONTAINER (LEFT) */
|
|
88
|
-
/* ============================================ */
|
|
89
|
-
|
|
90
|
-
.ns-org-hero__image {
|
|
91
|
-
/* Apple card style - rounded corners */
|
|
92
|
-
border-radius: var(--ns-border-radius-md);
|
|
93
|
-
overflow: hidden;
|
|
94
|
-
position: relative;
|
|
95
|
-
|
|
96
|
-
/* Mobile: ensure minimum height for empty state */
|
|
97
|
-
min-height: 100%;
|
|
98
|
-
|
|
99
|
-
background: linear-gradient(
|
|
100
|
-
180deg,
|
|
101
|
-
var(--ns-color-grey-900, #2d3748) 0%,
|
|
102
|
-
var(--ns-color-black, #1a202c) 100%
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
/* Layout */
|
|
106
|
-
display: flex;
|
|
107
|
-
align-items: center;
|
|
108
|
-
justify-content: center;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/* Tablet and up: auto height (grid handles it) */
|
|
112
|
-
@media (min-width: 768px) {
|
|
113
|
-
.ns-org-hero__image {
|
|
114
|
-
min-height: auto;
|
|
115
|
-
flex: initial;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
.ns-org-hero__image--no-image {
|
|
120
|
-
position: absolute;
|
|
121
|
-
top: 50%;
|
|
122
|
-
left: 50%;
|
|
123
|
-
transform: translate(-50%, -50%);
|
|
124
|
-
color: white;
|
|
125
|
-
z-index: 2;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
.ns-org-hero__image-content {
|
|
129
|
-
position: absolute;
|
|
130
|
-
bottom: 0;
|
|
131
|
-
left: 0;
|
|
132
|
-
right: 0;
|
|
133
|
-
padding: var(--ns-spacing-xl);
|
|
134
|
-
z-index: 3;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.ns-org-hero__image-title {
|
|
138
|
-
margin: 0;
|
|
139
|
-
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
.ns-org-hero__sub-title,
|
|
143
|
-
.ns-org-hero__image-title {
|
|
144
|
-
color: var(--ns-color-white);
|
|
145
|
-
color: var(--ns-color-white);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
.ns-org-hero__sub-title {
|
|
149
|
-
margin: 0;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/* ============================================ */
|
|
153
|
-
/* CONTENT AREA (RIGHT) */
|
|
154
|
-
/* ============================================ */
|
|
155
|
-
|
|
156
|
-
.ns-org-hero__content {
|
|
157
|
-
/* Content area for text, purchase form, etc. */
|
|
158
|
-
display: flex;
|
|
159
|
-
flex-direction: column;
|
|
160
|
-
padding: var(--ns-spacing-md);
|
|
161
|
-
}
|
|
162
|
-
</style>
|