@aurelia/storybook 1.0.2 → 2.1.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 (71) hide show
  1. package/CONTINUITY.md +22 -0
  2. package/README.md +209 -162
  3. package/apps/hello-world/.storybook/main.ts +0 -1
  4. package/apps/hello-world/package-lock.json +4591 -2732
  5. package/apps/hello-world/package.json +12 -22
  6. package/apps/hello-world/src/components/feedback-form.html +111 -0
  7. package/apps/hello-world/src/components/feedback-form.ts +45 -0
  8. package/apps/hello-world/src/components/notification-center.html +119 -0
  9. package/apps/hello-world/src/components/notification-center.ts +27 -0
  10. package/apps/hello-world/src/components/stat-card.html +107 -0
  11. package/apps/hello-world/src/components/stat-card.ts +33 -0
  12. package/apps/hello-world/src/components/weather-widget.html +89 -0
  13. package/apps/hello-world/src/components/weather-widget.ts +30 -0
  14. package/apps/hello-world/src/hello-world.html +44 -2
  15. package/apps/hello-world/src/services/weather-service.ts +15 -0
  16. package/apps/hello-world/src/stories/feedback-form.stories.ts +52 -0
  17. package/apps/hello-world/src/stories/hello-world.stories.ts +4 -5
  18. package/apps/hello-world/src/stories/notification-center.stories.ts +81 -0
  19. package/apps/hello-world/src/stories/stat-card.stories.ts +65 -0
  20. package/apps/hello-world/src/stories/weather-widget.stories.ts +57 -0
  21. package/apps/hello-world/tsconfig.json +4 -3
  22. package/apps/hello-world/vite.config.ts +0 -2
  23. package/apps/hello-world-webpack/.storybook/main.ts +0 -1
  24. package/apps/hello-world-webpack/package-lock.json +3723 -913
  25. package/apps/hello-world-webpack/package.json +6 -9
  26. package/apps/hello-world-webpack/src/components/feedback-form.html +111 -0
  27. package/apps/hello-world-webpack/src/components/feedback-form.ts +45 -0
  28. package/apps/hello-world-webpack/src/components/notification-center.html +119 -0
  29. package/apps/hello-world-webpack/src/components/notification-center.ts +27 -0
  30. package/apps/hello-world-webpack/src/components/stat-card.html +107 -0
  31. package/apps/hello-world-webpack/src/components/stat-card.ts +33 -0
  32. package/apps/hello-world-webpack/src/components/weather-widget.html +89 -0
  33. package/apps/hello-world-webpack/src/components/weather-widget.ts +30 -0
  34. package/apps/hello-world-webpack/src/hello-world.html +44 -2
  35. package/apps/hello-world-webpack/src/services/weather-service.ts +15 -0
  36. package/apps/hello-world-webpack/src/stories/feedback-form.stories.ts +52 -0
  37. package/apps/hello-world-webpack/src/stories/hello-world.stories.ts +5 -6
  38. package/apps/hello-world-webpack/src/stories/notification-center.stories.ts +81 -0
  39. package/apps/hello-world-webpack/src/stories/stat-card.stories.ts +65 -0
  40. package/apps/hello-world-webpack/src/stories/weather-widget.stories.ts +57 -0
  41. package/apps/hello-world-webpack/tsconfig.json +1 -1
  42. package/dist/index.js +4 -11
  43. package/dist/index.js.map +1 -1
  44. package/dist/preset.js +2 -9
  45. package/dist/preset.js.map +1 -1
  46. package/dist/preview/render.js +4 -9
  47. package/dist/preview/render.js.map +1 -1
  48. package/dist/preview/types.js +0 -1
  49. package/dist/preview/types.js.map +1 -1
  50. package/dist/preview.js +4 -7
  51. package/dist/preview.js.map +1 -1
  52. package/dist/webpack.js +1 -3
  53. package/dist/webpack.js.map +1 -1
  54. package/package.json +16 -19
  55. package/rollup.config.mjs +4 -7
  56. package/src/preset.ts +1 -1
  57. package/tsconfig.json +1 -1
  58. package/apps/hello-world/.yarnrc.yml +0 -2
  59. package/apps/hello-world-webpack/.yarnrc.yml +0 -2
  60. package/dist/index.mjs +0 -132
  61. package/dist/index.mjs.map +0 -1
  62. package/dist/preset.mjs +0 -60
  63. package/dist/preset.mjs.map +0 -1
  64. package/dist/preview/render.mjs +0 -114
  65. package/dist/preview/render.mjs.map +0 -1
  66. package/dist/preview/types.mjs +0 -2
  67. package/dist/preview/types.mjs.map +0 -1
  68. package/dist/preview.mjs +0 -114
  69. package/dist/preview.mjs.map +0 -1
  70. package/dist/webpack.mjs +0 -21
  71. package/dist/webpack.mjs.map +0 -1
@@ -8,16 +8,13 @@
8
8
  },
9
9
  "license": "UNLICENSED",
10
10
  "dependencies": {
11
- "@aurelia/router": "2.0.0-beta.24",
12
- "aurelia": "2.0.0-beta.24"
11
+ "@aurelia/router": "2.0.0-beta.25",
12
+ "aurelia": "2.0.0-beta.25"
13
13
  },
14
14
  "devDependencies": {
15
- "@aurelia/storybook": "^1.0.2",
16
- "@aurelia/testing": "2.0.0-beta.24",
17
- "@aurelia/webpack-loader": "2.0.0-beta.24",
18
- "@storybook/addon-actions": "^9.0.8",
19
- "@storybook/addon-links": "^9.0.0",
20
- "@storybook/test": "^9.0.0-alpha.2",
15
+ "@aurelia/storybook": "^2.0.0",
16
+ "@aurelia/testing": "2.0.0-beta.25",
17
+ "@aurelia/webpack-loader": "2.0.0-beta.25",
21
18
  "@types/node": "^22.10.2",
22
19
  "autoprefixer": "^10.4.20",
23
20
  "css-loader": "^7.1.2",
@@ -27,7 +24,7 @@
27
24
  "html-webpack-plugin": "^5.6.3",
28
25
  "postcss": "^8.4.49",
29
26
  "postcss-loader": "^8.1.1",
30
- "storybook": "^9.0.0",
27
+ "storybook": "^10.0.0",
31
28
  "style-loader": "^4.0.0",
32
29
  "stylelint": "^16.12.0",
33
30
  "stylelint-config-standard": "^36.0.1",
@@ -0,0 +1,111 @@
1
+ <style>
2
+ .feedback-form {
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: 18px;
6
+ padding: 28px;
7
+ border-radius: 18px;
8
+ background: #fff;
9
+ border: 1px solid rgba(15, 23, 42, 0.08);
10
+ box-shadow: 0 15px 40px rgba(15, 23, 42, 0.08);
11
+ max-width: 420px;
12
+ font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
13
+ }
14
+
15
+ .feedback-form label {
16
+ display: flex;
17
+ flex-direction: column;
18
+ gap: 8px;
19
+ font-size: 14px;
20
+ color: #0f172a;
21
+ font-weight: 600;
22
+ }
23
+
24
+ .feedback-form input,
25
+ .feedback-form select,
26
+ .feedback-form textarea {
27
+ border-radius: 12px;
28
+ border: 1px solid rgba(15, 23, 42, 0.15);
29
+ padding: 12px 14px;
30
+ font-size: 15px;
31
+ font-weight: 500;
32
+ color: #0f172a;
33
+ background: #f8fafc;
34
+ transition: border 0.2s ease, box-shadow 0.2s ease;
35
+ }
36
+
37
+ .feedback-form input:focus,
38
+ .feedback-form select:focus,
39
+ .feedback-form textarea:focus {
40
+ outline: none;
41
+ border-color: #2563eb;
42
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);
43
+ background: #fff;
44
+ }
45
+
46
+ .feedback-form__actions {
47
+ display: flex;
48
+ gap: 12px;
49
+ }
50
+
51
+ .feedback-form__actions button {
52
+ flex: 1;
53
+ border: none;
54
+ border-radius: 12px;
55
+ padding: 12px 16px;
56
+ font-size: 15px;
57
+ font-weight: 600;
58
+ cursor: pointer;
59
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
60
+ }
61
+
62
+ .feedback-form__actions button:first-child {
63
+ background: linear-gradient(135deg, #2563eb, #4f46e5);
64
+ color: #fff;
65
+ box-shadow: 0 15px 30px rgba(79, 70, 229, 0.3);
66
+ }
67
+
68
+ .feedback-form__actions button:first-child:disabled {
69
+ opacity: 0.65;
70
+ box-shadow: none;
71
+ cursor: not-allowed;
72
+ }
73
+
74
+ .feedback-form__actions button:last-child {
75
+ background: #e2e8f0;
76
+ color: #0f172a;
77
+ }
78
+
79
+ .feedback-form__success {
80
+ margin: 0;
81
+ font-size: 14px;
82
+ font-weight: 600;
83
+ color: #059669;
84
+ text-align: center;
85
+ }
86
+ </style>
87
+ <form class="feedback-form" submit.trigger="submit($event)" aria-live="polite">
88
+ <label>
89
+ Name
90
+ <input type="text" value.two-way="form.name" placeholder="Ada Lovelace" required />
91
+ </label>
92
+ <label>
93
+ Email
94
+ <input type="email" value.two-way="form.email" placeholder="ada@example.com" required />
95
+ </label>
96
+ <label>
97
+ Topic
98
+ <select value.two-way="form.topic">
99
+ <option repeat.for="topic of topics" value.bind="topic">${topic}</option>
100
+ </select>
101
+ </label>
102
+ <label>
103
+ Message
104
+ <textarea rows="4" value.two-way="form.message"></textarea>
105
+ </label>
106
+ <div class="feedback-form__actions">
107
+ <button type="submit" disabled.bind="submitting">${submitting ? 'Sending...' : 'Send feedback'}</button>
108
+ <button type="button" click.trigger="reset()" disabled.bind="submitting">Reset</button>
109
+ </div>
110
+ <p if.bind="submitted" class="feedback-form__success">Thank you for the feedback!</p>
111
+ </form>
@@ -0,0 +1,45 @@
1
+ import { bindable } from 'aurelia';
2
+
3
+ export interface FeedbackPayload {
4
+ name: string;
5
+ email: string;
6
+ topic: string;
7
+ message: string;
8
+ }
9
+
10
+ export class FeedbackForm {
11
+ @bindable() topics: string[] = ['Bug report', 'Feature idea', 'General praise'];
12
+ @bindable() submitting = false;
13
+ @bindable() onSubmit?: (payload: FeedbackPayload) => Promise<void> | void;
14
+
15
+ form: FeedbackPayload = {
16
+ name: '',
17
+ email: '',
18
+ topic: 'Bug report',
19
+ message: '',
20
+ };
21
+
22
+ submitted = false;
23
+
24
+ async submit(event?: Event) {
25
+ event?.preventDefault();
26
+ if (this.submitting) {
27
+ return;
28
+ }
29
+
30
+ this.submitting = true;
31
+ await this.onSubmit?.({ ...this.form });
32
+ this.submitting = false;
33
+ this.submitted = true;
34
+ }
35
+
36
+ reset() {
37
+ this.form = {
38
+ name: '',
39
+ email: '',
40
+ topic: this.topics[0] ?? '',
41
+ message: '',
42
+ };
43
+ this.submitted = false;
44
+ }
45
+ }
@@ -0,0 +1,119 @@
1
+ <style>
2
+ .notification-center {
3
+ width: 420px;
4
+ border-radius: 20px;
5
+ background: #0f172a;
6
+ color: #f8fafc;
7
+ padding: 20px 24px;
8
+ box-shadow: 0 25px 50px rgba(15, 23, 42, 0.45);
9
+ font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
10
+ }
11
+
12
+ .notification-center__header {
13
+ display: flex;
14
+ justify-content: space-between;
15
+ margin-bottom: 18px;
16
+ }
17
+
18
+ .notification-center__header h2 {
19
+ margin: 0;
20
+ font-size: 20px;
21
+ }
22
+
23
+ .notification-center__count {
24
+ font-size: 13px;
25
+ opacity: 0.7;
26
+ align-self: center;
27
+ }
28
+
29
+ .notification-center__list {
30
+ list-style: none;
31
+ margin: 0;
32
+ padding: 0;
33
+ display: flex;
34
+ flex-direction: column;
35
+ gap: 12px;
36
+ }
37
+
38
+ .notification-center__item {
39
+ display: flex;
40
+ justify-content: space-between;
41
+ padding: 14px 16px;
42
+ border-radius: 14px;
43
+ background: rgba(148, 163, 184, 0.12);
44
+ border: 1px solid rgba(148, 163, 184, 0.2);
45
+ gap: 12px;
46
+ }
47
+
48
+ .notification-center__item strong {
49
+ display: block;
50
+ font-size: 15px;
51
+ margin-bottom: 4px;
52
+ }
53
+
54
+ .notification-center__item p {
55
+ margin: 0 0 6px;
56
+ font-size: 14px;
57
+ opacity: 0.9;
58
+ }
59
+
60
+ .notification-center__item small {
61
+ font-size: 12px;
62
+ opacity: 0.6;
63
+ }
64
+
65
+ .notification-center__item button {
66
+ background: transparent;
67
+ border: 1px solid rgba(255, 255, 255, 0.4);
68
+ color: inherit;
69
+ border-radius: 999px;
70
+ padding: 4px 12px;
71
+ height: fit-content;
72
+ cursor: pointer;
73
+ transition: background 0.2s ease;
74
+ }
75
+
76
+ .notification-center__item button:hover,
77
+ .notification-center__item button:focus-visible {
78
+ background: rgba(255, 255, 255, 0.1);
79
+ }
80
+
81
+ .notification-center__item.success {
82
+ border-color: rgba(45, 212, 191, 0.4);
83
+ }
84
+
85
+ .notification-center__item.warning {
86
+ border-color: rgba(249, 115, 22, 0.4);
87
+ }
88
+
89
+ .notification-center__item.error {
90
+ border-color: rgba(239, 68, 68, 0.4);
91
+ }
92
+
93
+ .notification-center__empty {
94
+ text-align: center;
95
+ padding: 16px;
96
+ background: rgba(148, 163, 184, 0.1);
97
+ border-radius: 12px;
98
+ font-size: 14px;
99
+ }
100
+ </style>
101
+ <section class="notification-center">
102
+ <header class="notification-center__header">
103
+ <h2>Notifications</h2>
104
+ <span class="notification-center__count">${notifications.length} total</span>
105
+ </header>
106
+ <ul class="notification-center__list">
107
+ <li repeat.for="note of visibleNotifications" class="notification-center__item ${note.level}">
108
+ <div>
109
+ <strong>${note.title}</strong>
110
+ <p>${note.message}</p>
111
+ <small if.bind="showTimestamp">${note.timestamp}</small>
112
+ </div>
113
+ <button type="button" click.trigger="dismiss(note)">Dismiss</button>
114
+ </li>
115
+ <li if.bind="visibleNotifications.length === 0" class="notification-center__empty">
116
+ You're all caught up!
117
+ </li>
118
+ </ul>
119
+ </section>
@@ -0,0 +1,27 @@
1
+ import { bindable } from 'aurelia';
2
+
3
+ export type NotificationLevel = 'info' | 'success' | 'warning' | 'error';
4
+
5
+ export interface NotificationItem {
6
+ id: number;
7
+ title: string;
8
+ message: string;
9
+ level: NotificationLevel;
10
+ timestamp?: string;
11
+ }
12
+
13
+ export class NotificationCenter {
14
+ @bindable() notifications: NotificationItem[] = [];
15
+ @bindable() maxVisible = 4;
16
+ @bindable() showTimestamp = true;
17
+ @bindable() onDismiss?: (notification: NotificationItem) => void;
18
+
19
+ get visibleNotifications() {
20
+ return this.notifications.slice(0, this.maxVisible);
21
+ }
22
+
23
+ dismiss(notification: NotificationItem) {
24
+ this.notifications = this.notifications.filter((note) => note.id !== notification.id);
25
+ this.onDismiss?.(notification);
26
+ }
27
+ }
@@ -0,0 +1,107 @@
1
+ <style>
2
+ .stat-card {
3
+ border-radius: 16px;
4
+ padding: 20px 24px;
5
+ background: linear-gradient(135deg, #1f3d8f, #3c74ff);
6
+ color: #fff;
7
+ box-shadow: 0 20px 45px rgba(13, 32, 94, 0.35);
8
+ max-width: 360px;
9
+ font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
10
+ }
11
+
12
+ .stat-card__header {
13
+ display: flex;
14
+ justify-content: space-between;
15
+ gap: 12px;
16
+ align-items: flex-start;
17
+ }
18
+
19
+ .stat-card__label {
20
+ text-transform: uppercase;
21
+ letter-spacing: 0.08em;
22
+ font-size: 12px;
23
+ opacity: 0.8;
24
+ margin: 0 0 6px;
25
+ }
26
+
27
+ .stat-card__value {
28
+ font-size: 44px;
29
+ margin: 0;
30
+ font-weight: 600;
31
+ line-height: 1;
32
+ }
33
+
34
+ .stat-card__unit {
35
+ font-size: 20px;
36
+ margin-left: 6px;
37
+ opacity: 0.85;
38
+ }
39
+
40
+ .stat-card__refresh {
41
+ border: 1px solid rgba(255, 255, 255, 0.4);
42
+ background: rgba(255, 255, 255, 0.12);
43
+ color: #fff;
44
+ padding: 6px 12px;
45
+ border-radius: 999px;
46
+ font-size: 12px;
47
+ letter-spacing: 0.05em;
48
+ text-transform: uppercase;
49
+ cursor: pointer;
50
+ transition: background 0.2s ease, border 0.2s ease;
51
+ }
52
+
53
+ .stat-card__refresh:focus-visible,
54
+ .stat-card__refresh:hover {
55
+ background: rgba(255, 255, 255, 0.2);
56
+ border-color: rgba(255, 255, 255, 0.7);
57
+ }
58
+
59
+ .stat-card__description {
60
+ margin: 18px 0 12px;
61
+ font-size: 15px;
62
+ opacity: 0.9;
63
+ }
64
+
65
+ .stat-card__footer {
66
+ display: inline-flex;
67
+ align-items: center;
68
+ gap: 8px;
69
+ border-radius: 999px;
70
+ padding: 6px 14px;
71
+ font-size: 14px;
72
+ font-weight: 500;
73
+ }
74
+
75
+ .stat-card__footer.positive {
76
+ background: rgba(20, 235, 178, 0.2);
77
+ color: #14ebb2;
78
+ }
79
+
80
+ .stat-card__footer.negative {
81
+ background: rgba(255, 107, 107, 0.2);
82
+ color: #ff6b6b;
83
+ }
84
+
85
+ .stat-card__footer.neutral {
86
+ background: rgba(255, 255, 255, 0.2);
87
+ color: #fff;
88
+ }
89
+ </style>
90
+ <section class="stat-card" aria-live="polite">
91
+ <header class="stat-card__header">
92
+ <div>
93
+ <p class="stat-card__label">${title}</p>
94
+ <h2 class="stat-card__value">
95
+ ${value}<span if.bind="unit" class="stat-card__unit">${unit}</span>
96
+ </h2>
97
+ </div>
98
+ <button type="button" class="stat-card__refresh" click.trigger="refresh()" title="Refresh metric" aria-label="Refresh metric">
99
+ Refresh
100
+ </button>
101
+ </header>
102
+ <p class="stat-card__description">${description}</p>
103
+ <footer class="stat-card__footer ${changeState}">
104
+ <strong>${changeLabel}</strong>
105
+ <span>${changeCopy}</span>
106
+ </footer>
107
+ </section>
@@ -0,0 +1,33 @@
1
+ import { bindable } from 'aurelia';
2
+
3
+ type TrendState = 'positive' | 'negative' | 'neutral';
4
+
5
+ export class StatCard {
6
+ @bindable() title = 'Active users';
7
+ @bindable() value: number | string = 0;
8
+ @bindable() unit = '';
9
+ @bindable() change = 0; // percent delta
10
+ @bindable() description = '';
11
+ @bindable() changeCopy = 'vs last week';
12
+ @bindable() onRefresh?: () => void;
13
+
14
+ refresh() {
15
+ this.onRefresh?.();
16
+ }
17
+
18
+ get changeLabel() {
19
+ const rounded = Number(this.change).toFixed(1).replace(/\.0$/, '');
20
+ const sign = this.change > 0 ? '+' : '';
21
+ return `${sign}${rounded}%`;
22
+ }
23
+
24
+ get changeState(): TrendState {
25
+ if (this.change > 0) {
26
+ return 'positive';
27
+ }
28
+ if (this.change < 0) {
29
+ return 'negative';
30
+ }
31
+ return 'neutral';
32
+ }
33
+ }
@@ -0,0 +1,89 @@
1
+ <style>
2
+ .weather-widget {
3
+ width: 320px;
4
+ border-radius: 24px;
5
+ padding: 22px;
6
+ background: linear-gradient(160deg, #fef3c7, #fcd34d);
7
+ color: #1f2937;
8
+ box-shadow: 0 20px 45px rgba(244, 114, 10, 0.25);
9
+ font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
10
+ }
11
+
12
+ .weather-widget header {
13
+ display: flex;
14
+ justify-content: space-between;
15
+ align-items: center;
16
+ margin-bottom: 14px;
17
+ }
18
+
19
+ .weather-widget header h2 {
20
+ margin: 0;
21
+ font-size: 20px;
22
+ font-weight: 600;
23
+ }
24
+
25
+ .weather-widget button {
26
+ border: none;
27
+ background: rgba(255, 255, 255, 0.4);
28
+ color: #92400e;
29
+ border-radius: 12px;
30
+ padding: 6px 12px;
31
+ font-weight: 600;
32
+ cursor: pointer;
33
+ transition: background 0.2s ease;
34
+ }
35
+
36
+ .weather-widget button:disabled {
37
+ opacity: 0.6;
38
+ cursor: not-allowed;
39
+ }
40
+
41
+ .weather-widget button:not(:disabled):hover,
42
+ .weather-widget button:not(:disabled):focus-visible {
43
+ background: rgba(255, 255, 255, 0.6);
44
+ }
45
+
46
+ .weather-widget__state {
47
+ padding: 16px;
48
+ border-radius: 16px;
49
+ background: rgba(255, 255, 255, 0.6);
50
+ font-weight: 600;
51
+ text-align: center;
52
+ }
53
+
54
+ .weather-widget__state--error {
55
+ background: rgba(248, 113, 113, 0.3);
56
+ color: #991b1b;
57
+ }
58
+
59
+ .weather-widget__body {
60
+ display: flex;
61
+ flex-direction: column;
62
+ gap: 6px;
63
+ font-weight: 600;
64
+ }
65
+
66
+ .weather-widget__temp {
67
+ font-size: 64px;
68
+ margin: 0;
69
+ font-weight: 700;
70
+ }
71
+
72
+ .weather-widget__body small {
73
+ font-size: 14px;
74
+ opacity: 0.8;
75
+ }
76
+ </style>
77
+ <section class="weather-widget" aria-live="polite">
78
+ <header>
79
+ <h2>${location}</h2>
80
+ <button type="button" click.trigger="refresh()" disabled.bind="state === 'loading'">Refresh</button>
81
+ </header>
82
+ <div if.bind="state === 'loading'" class="weather-widget__state">Loading latest data...</div>
83
+ <div if.bind="state === 'error'" class="weather-widget__state weather-widget__state--error">${error}</div>
84
+ <div if.bind="state === 'ready'" class="weather-widget__body">
85
+ <p class="weather-widget__temp">${report.temperature}&deg;</p>
86
+ <p>${report.condition}</p>
87
+ <small>High ${report.high}&deg; - Low ${report.low}&deg;</small>
88
+ </div>
89
+ </section>
@@ -0,0 +1,30 @@
1
+ import { bindable } from 'aurelia';
2
+ import { IWeatherService, WeatherSummary, WeatherService } from '../services/weather-service';
3
+
4
+ export class WeatherWidget {
5
+ static inject = [IWeatherService];
6
+
7
+ constructor(private readonly service: WeatherService) {}
8
+
9
+ @bindable() location = 'Seattle, WA';
10
+
11
+ report: WeatherSummary | null = null;
12
+ state: 'idle' | 'loading' | 'ready' | 'error' = 'idle';
13
+ error = '';
14
+
15
+ binding() {
16
+ void this.refresh();
17
+ }
18
+
19
+ async refresh() {
20
+ try {
21
+ this.state = 'loading';
22
+ this.error = '';
23
+ this.report = await this.service.getWeather(this.location);
24
+ this.state = 'ready';
25
+ } catch (err) {
26
+ this.state = 'error';
27
+ this.error = err instanceof Error ? err.message : 'Unable to load weather data.';
28
+ }
29
+ }
30
+ }
@@ -1,6 +1,48 @@
1
- <div style="border: 1px solid #ccc; padding: 16px; border-radius: 4px;">
1
+ <style>
2
+ .hello-card {
3
+ max-width: 360px;
4
+ padding: 32px;
5
+ border-radius: 24px;
6
+ background: linear-gradient(135deg, #ec4899, #a855f7);
7
+ color: #fff;
8
+ font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
9
+ box-shadow: 0 25px 45px rgba(168, 85, 247, 0.35);
10
+ }
11
+
12
+ .hello-card h1 {
13
+ margin: 0 0 12px;
14
+ font-size: 28px;
15
+ font-weight: 600;
16
+ }
17
+
18
+ .hello-card p {
19
+ margin: 0 0 18px;
20
+ font-size: 16px;
21
+ opacity: 0.95;
22
+ }
23
+
24
+ .hello-card button {
25
+ border: none;
26
+ border-radius: 16px;
27
+ padding: 12px 18px;
28
+ font-size: 15px;
29
+ font-weight: 600;
30
+ background: #fff;
31
+ color: #a855f7;
32
+ cursor: pointer;
33
+ box-shadow: 0 15px 30px rgba(255, 255, 255, 0.25);
34
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
35
+ }
36
+
37
+ .hello-card button:hover,
38
+ .hello-card button:focus-visible {
39
+ transform: translateY(-2px);
40
+ box-shadow: 0 18px 32px rgba(255, 255, 255, 0.35);
41
+ }
42
+ </style>
43
+ <div class="hello-card">
2
44
  <h1>${message}</h1>
3
45
  <p>Counter: ${counter}</p>
4
46
  <button click.trigger="increment()">Increment</button>
5
47
  <au-slot></au-slot>
6
- </div>
48
+ </div>
@@ -0,0 +1,15 @@
1
+ import { DI } from 'aurelia';
2
+
3
+ export interface WeatherSummary {
4
+ location: string;
5
+ condition: string;
6
+ temperature: number;
7
+ high: number;
8
+ low: number;
9
+ }
10
+
11
+ export interface WeatherService {
12
+ getWeather(location: string): Promise<WeatherSummary>;
13
+ }
14
+
15
+ export const IWeatherService = DI.createInterface<WeatherService>('IWeatherService');