@adarsh.nishad/activityual-ui 0.0.0-stage → 0.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.
@@ -0,0 +1,144 @@
1
+ import { LitElement, html, css } from 'lit';
2
+ export class ActivityualFormField extends LitElement {
3
+ constructor() {
4
+ super(...arguments);
5
+ this.label = '';
6
+ this.name = '';
7
+ this.type = 'text';
8
+ this.value = '';
9
+ this.placeholder = '';
10
+ this.error = '';
11
+ this.required = false;
12
+ this.disabled = false;
13
+ }
14
+ static { this.properties = {
15
+ label: { type: String },
16
+ name: { type: String },
17
+ type: { type: String },
18
+ value: { type: String },
19
+ placeholder: { type: String },
20
+ error: { type: String },
21
+ required: { type: Boolean },
22
+ disabled: { type: Boolean }
23
+ }; }
24
+ static { this.styles = css `
25
+ :host {
26
+ display: block;
27
+ box-sizing: border-box;
28
+ margin-bottom: 1.15rem;
29
+ }
30
+
31
+ .field-group {
32
+ display: flex;
33
+ flex-direction: column;
34
+ gap: 0.35rem;
35
+ font-family: Inter, 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
36
+ }
37
+
38
+ label {
39
+ font-size: 11.5px;
40
+ font-weight: 600;
41
+ color: #f5f5f5;
42
+ display: flex;
43
+ align-items: center;
44
+ gap: 0.25rem;
45
+ }
46
+
47
+ .required-star {
48
+ color: #ff3434;
49
+ }
50
+
51
+ .input-trough {
52
+ background: #121212;
53
+ border: 1px solid rgba(255, 255, 255, 0.06);
54
+ border-radius: 10px;
55
+ padding: 0 0.85rem;
56
+ height: 40px;
57
+ display: flex;
58
+ align-items: center;
59
+ box-shadow:
60
+ inset 0 2px 4px rgba(0, 0, 0, 0.5),
61
+ 0 1px 1px rgba(255, 255, 255, 0.02);
62
+ transition: all 0.2s ease;
63
+ }
64
+
65
+ .input-trough:focus-within {
66
+ border-color: #ff4305;
67
+ box-shadow:
68
+ 0 0 0 2px rgba(255, 67, 5, 0.25),
69
+ inset 0 1px 2px rgba(0, 0, 0, 0.4);
70
+ }
71
+
72
+ .has-error {
73
+ border-color: #ff3434 !important;
74
+ background: rgba(255, 52, 52, 0.06) !important;
75
+ box-shadow: 0 0 0 2px rgba(255, 52, 52, 0.2) !important;
76
+ }
77
+
78
+ input {
79
+ width: 100%;
80
+ background: transparent;
81
+ border: none;
82
+ outline: none;
83
+ font-family: inherit;
84
+ font-size: 13px;
85
+ font-weight: 500;
86
+ color: #f5f5f5;
87
+ }
88
+
89
+ input::placeholder {
90
+ color: #707070;
91
+ }
92
+
93
+ input:disabled {
94
+ opacity: 0.35;
95
+ cursor: not-allowed;
96
+ }
97
+
98
+ .error-message {
99
+ font-size: 10.5px;
100
+ font-weight: 500;
101
+ color: #ff3434;
102
+ margin-top: 0.2rem;
103
+ }
104
+ `; }
105
+ handleInput(e) {
106
+ const target = e.target;
107
+ this.value = target.value;
108
+ this.dispatchEvent(new CustomEvent('input-change', {
109
+ detail: { name: this.name, value: this.value },
110
+ bubbles: true,
111
+ composed: true
112
+ }));
113
+ }
114
+ render() {
115
+ return html `
116
+ <div class="field-group">
117
+ ${this.label
118
+ ? html `
119
+ <label for="${this.name}">
120
+ <span>${this.label}</span>
121
+ ${this.required ? html `<span class="required-star" aria-hidden="true">*</span>` : ''}
122
+ </label>
123
+ `
124
+ : ''}
125
+ <div class="input-trough ${this.error ? 'has-error' : ''}">
126
+ <input
127
+ id="${this.name}"
128
+ name="${this.name}"
129
+ type="${this.type}"
130
+ .value="${this.value}"
131
+ placeholder="${this.placeholder}"
132
+ ?required="${this.required}"
133
+ ?disabled="${this.disabled}"
134
+ @input="${this.handleInput}"
135
+ />
136
+ </div>
137
+ ${this.error ? html `<span class="error-message" role="alert">${this.error}</span>` : ''}
138
+ </div>
139
+ `;
140
+ }
141
+ }
142
+ if (!customElements.get('activityual-form-field')) {
143
+ customElements.define('activityual-form-field', ActivityualFormField);
144
+ }
@@ -0,0 +1,13 @@
1
+ export * from './tokens.js';
2
+ export * from './button.js';
3
+ export * from './card.js';
4
+ export * from './status-pill.js';
5
+ export * from './streak-badge.js';
6
+ export * from './progress-ring.js';
7
+ export * from './stat-card.js';
8
+ export * from './activity-card.js';
9
+ export * from './chart-panel.js';
10
+ export * from './recommendation-banner.js';
11
+ export * from './empty-state.js';
12
+ export * from './confirm-dialog.js';
13
+ export * from './form-field.js';
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ export * from './tokens.js';
2
+ export * from './button.js';
3
+ export * from './card.js';
4
+ export * from './status-pill.js';
5
+ export * from './streak-badge.js';
6
+ export * from './progress-ring.js';
7
+ export * from './stat-card.js';
8
+ export * from './activity-card.js';
9
+ export * from './chart-panel.js';
10
+ export * from './recommendation-banner.js';
11
+ export * from './empty-state.js';
12
+ export * from './confirm-dialog.js';
13
+ export * from './form-field.js';
@@ -0,0 +1,23 @@
1
+ import { LitElement } from 'lit';
2
+ export declare class ActivityualProgressRing extends LitElement {
3
+ static properties: {
4
+ percentage: {
5
+ type: NumberConstructor;
6
+ };
7
+ size: {
8
+ type: NumberConstructor;
9
+ };
10
+ strokeWidth: {
11
+ type: NumberConstructor;
12
+ };
13
+ label: {
14
+ type: StringConstructor;
15
+ };
16
+ };
17
+ percentage: number;
18
+ size: number;
19
+ strokeWidth: number;
20
+ label: string;
21
+ static styles: import("lit").CSSResult;
22
+ render(): import("lit-html").TemplateResult<1>;
23
+ }
@@ -0,0 +1,124 @@
1
+ import { LitElement, html, css } from 'lit';
2
+ export class ActivityualProgressRing extends LitElement {
3
+ constructor() {
4
+ super(...arguments);
5
+ this.percentage = 0;
6
+ this.size = 120;
7
+ this.strokeWidth = 10;
8
+ this.label = 'Rhythm';
9
+ }
10
+ static { this.properties = {
11
+ percentage: { type: Number },
12
+ size: { type: Number },
13
+ strokeWidth: { type: Number },
14
+ label: { type: String }
15
+ }; }
16
+ static { this.styles = css `
17
+ :host {
18
+ display: inline-block;
19
+ vertical-align: middle;
20
+ }
21
+
22
+ .container {
23
+ position: relative;
24
+ display: inline-flex;
25
+ align-items: center;
26
+ justify-content: center;
27
+ font-family: Inter, 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
28
+ user-select: none;
29
+ }
30
+
31
+ svg {
32
+ transform: rotate(-90deg);
33
+ }
34
+
35
+ .bg-circle {
36
+ fill: none;
37
+ stroke: rgba(255, 255, 255, 0.05);
38
+ }
39
+
40
+ .progress-circle {
41
+ fill: none;
42
+ stroke: #ff4305;
43
+ stroke-linecap: round;
44
+ transition: stroke-dashoffset 0.5s cubic-bezier(0.16, 1, 0.3, 1);
45
+ filter: drop-shadow(0 0 6px rgba(255, 67, 5, 0.4));
46
+ }
47
+
48
+ .progress-circle.complete {
49
+ stroke: #25d366;
50
+ filter: drop-shadow(0 0 6px rgba(37, 211, 102, 0.4));
51
+ }
52
+
53
+ .content {
54
+ position: absolute;
55
+ display: flex;
56
+ flex-direction: column;
57
+ align-items: center;
58
+ justify-content: center;
59
+ text-align: center;
60
+ }
61
+
62
+ .value {
63
+ font-size: 20px;
64
+ font-weight: 700;
65
+ color: #f5f5f5;
66
+ line-height: 1;
67
+ font-family: 'JetBrains Mono', monospace;
68
+ font-variant-numeric: tabular-nums;
69
+ }
70
+
71
+ .label-text {
72
+ font-size: 9.5px;
73
+ font-weight: 600;
74
+ color: #707070;
75
+ text-transform: uppercase;
76
+ letter-spacing: 0.05em;
77
+ margin-top: 3px;
78
+ }
79
+ `; }
80
+ render() {
81
+ const radius = (this.size - this.strokeWidth) / 2;
82
+ const circumference = 2 * Math.PI * radius;
83
+ const clampedPercent = Math.min(Math.max(this.percentage, 0), 100);
84
+ const strokeDashoffset = circumference - (clampedPercent / 100) * circumference;
85
+ const isComplete = clampedPercent >= 100;
86
+ return html `
87
+ <div
88
+ class="container"
89
+ style="width: ${this.size}px; height: ${this.size}px;"
90
+ role="progressbar"
91
+ aria-valuenow="${clampedPercent}"
92
+ aria-valuemin="0"
93
+ aria-valuemax="100"
94
+ aria-label="${this.label}: ${clampedPercent}%"
95
+ >
96
+ <svg width="${this.size}" height="${this.size}">
97
+ <circle
98
+ class="bg-circle"
99
+ cx="${this.size / 2}"
100
+ cy="${this.size / 2}"
101
+ r="${radius}"
102
+ stroke-width="${this.strokeWidth}"
103
+ ></circle>
104
+ <circle
105
+ class="progress-circle ${isComplete ? 'complete' : ''}"
106
+ cx="${this.size / 2}"
107
+ cy="${this.size / 2}"
108
+ r="${radius}"
109
+ stroke-width="${this.strokeWidth}"
110
+ stroke-dasharray="${circumference}"
111
+ stroke-dashoffset="${strokeDashoffset}"
112
+ ></circle>
113
+ </svg>
114
+ <div class="content">
115
+ <span class="value">${clampedPercent}%</span>
116
+ <span class="label-text">${this.label}</span>
117
+ </div>
118
+ </div>
119
+ `;
120
+ }
121
+ }
122
+ if (!customElements.get('activityual-progress-ring')) {
123
+ customElements.define('activityual-progress-ring', ActivityualProgressRing);
124
+ }
@@ -0,0 +1,31 @@
1
+ import { LitElement } from 'lit';
2
+ export declare class ActivityualRecommendationBanner extends LitElement {
3
+ static properties: {
4
+ activityTitle: {
5
+ type: StringConstructor;
6
+ };
7
+ suggestedTime: {
8
+ type: StringConstructor;
9
+ };
10
+ reason: {
11
+ type: StringConstructor;
12
+ };
13
+ sampleSize: {
14
+ type: NumberConstructor;
15
+ };
16
+ status: {
17
+ type: StringConstructor;
18
+ };
19
+ generatedAt: {
20
+ type: StringConstructor;
21
+ };
22
+ };
23
+ activityTitle: string;
24
+ suggestedTime: string;
25
+ reason: string;
26
+ sampleSize: number;
27
+ status: 'available' | 'insufficient_history';
28
+ generatedAt: string;
29
+ static styles: import("lit").CSSResult;
30
+ render(): import("lit-html").TemplateResult<1>;
31
+ }
@@ -0,0 +1,192 @@
1
+ import { LitElement, html, css } from 'lit';
2
+ export class ActivityualRecommendationBanner extends LitElement {
3
+ constructor() {
4
+ super(...arguments);
5
+ this.activityTitle = '';
6
+ this.suggestedTime = '';
7
+ this.reason = '';
8
+ this.sampleSize = 0;
9
+ this.status = 'available';
10
+ this.generatedAt = '';
11
+ }
12
+ static { this.properties = {
13
+ activityTitle: { type: String },
14
+ suggestedTime: { type: String },
15
+ reason: { type: String },
16
+ sampleSize: { type: Number },
17
+ status: { type: String },
18
+ generatedAt: { type: String }
19
+ }; }
20
+ static { this.styles = css `
21
+ :host {
22
+ display: block;
23
+ box-sizing: border-box;
24
+ }
25
+
26
+ .banner {
27
+ background: linear-gradient(145deg, rgba(42, 42, 42, 0.65) 0%, rgba(24, 24, 24, 0.8) 100%);
28
+ backdrop-filter: blur(20px);
29
+ -webkit-backdrop-filter: blur(20px);
30
+ border: 1px solid rgba(255, 255, 255, 0.05);
31
+ border-left: 3px solid #ff4305;
32
+ border-radius: 16px;
33
+ padding: 1.25rem 1.5rem;
34
+ box-shadow:
35
+ 0 10px 28px -4px rgba(0, 0, 0, 0.45),
36
+ 0 0 16px rgba(255, 67, 5, 0.08);
37
+ font-family: Inter, 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
38
+ position: relative;
39
+ overflow: hidden;
40
+ box-sizing: border-box;
41
+ }
42
+
43
+ .insufficient {
44
+ border-left: 3px solid #555555;
45
+ border-style: dashed;
46
+ }
47
+
48
+ .header {
49
+ display: flex;
50
+ align-items: center;
51
+ justify-content: space-between;
52
+ margin-bottom: 0.65rem;
53
+ }
54
+
55
+ .badge {
56
+ display: inline-flex;
57
+ align-items: center;
58
+ gap: 0.4rem;
59
+ font-size: 10px;
60
+ font-weight: 700;
61
+ text-transform: uppercase;
62
+ letter-spacing: 0.05em;
63
+ padding: 0.2rem 0.65rem;
64
+ border-radius: 9999px;
65
+ background: rgba(255, 67, 5, 0.12);
66
+ color: #ff4305;
67
+ border: 1px solid rgba(255, 67, 5, 0.25);
68
+ font-family: 'JetBrains Mono', monospace;
69
+ }
70
+
71
+ .sample-tag {
72
+ font-size: 10.5px;
73
+ font-weight: 600;
74
+ background: rgba(255, 255, 255, 0.05);
75
+ color: #a5a5a5;
76
+ border: 1px solid rgba(255, 255, 255, 0.06);
77
+ padding: 0.15rem 0.55rem;
78
+ border-radius: 9999px;
79
+ font-family: 'JetBrains Mono', monospace;
80
+ }
81
+
82
+ .title {
83
+ font-size: 15px;
84
+ font-weight: 700;
85
+ letter-spacing: -0.015em;
86
+ color: #f5f5f5;
87
+ margin: 0 0 0.4rem 0;
88
+ line-height: 1.3;
89
+ }
90
+
91
+ .explanation {
92
+ font-size: 12.5px;
93
+ line-height: 1.5;
94
+ color: #a5a5a5;
95
+ margin: 0;
96
+ }
97
+
98
+ .time-pod {
99
+ margin-top: 0.85rem;
100
+ display: inline-flex;
101
+ align-items: center;
102
+ gap: 0.65rem;
103
+ padding: 0.4rem 0.85rem;
104
+ border-radius: 9999px;
105
+ background: #252525;
106
+ border: 1px solid rgba(255, 255, 255, 0.08);
107
+ color: #ffffff;
108
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
109
+ }
110
+
111
+ .time-label {
112
+ font-size: 10px;
113
+ font-weight: 600;
114
+ text-transform: uppercase;
115
+ letter-spacing: 0.04em;
116
+ color: #ff4305;
117
+ font-family: 'JetBrains Mono', monospace;
118
+ }
119
+
120
+ .time-val {
121
+ font-size: 12px;
122
+ font-weight: 700;
123
+ color: #ffffff;
124
+ font-family: 'JetBrains Mono', monospace;
125
+ }
126
+
127
+ .footer {
128
+ margin-top: 0.85rem;
129
+ font-size: 10.5px;
130
+ color: #707070;
131
+ display: flex;
132
+ align-items: center;
133
+ justify-content: space-between;
134
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
135
+ padding-top: 0.65rem;
136
+ font-family: 'JetBrains Mono', monospace;
137
+ }
138
+ `; }
139
+ render() {
140
+ if (this.status === 'insufficient_history') {
141
+ return html `
142
+ <div class="banner insufficient">
143
+ <div class="header">
144
+ <span class="badge" style="background: rgba(255,255,255,0.06); color: #a5a5a5; border-color: rgba(255,255,255,0.1);">
145
+ Gathering Data
146
+ </span>
147
+ <span class="sample-tag">${this.sampleSize} / 5 records</span>
148
+ </div>
149
+ <h3 class="title">Personalized Rhythm Learning</h3>
150
+ <p class="explanation">
151
+ Activityual needs at least 5 records of <strong>${this.activityTitle}</strong>
152
+ before computing personalized circadian timing recommendations. Keep tracking!
153
+ </p>
154
+ </div>
155
+ `;
156
+ }
157
+ return html `
158
+ <div class="banner">
159
+ <div class="header">
160
+ <span class="badge">Optimization Suggestion</span>
161
+ ${this.sampleSize > 0
162
+ ? html `<span class="sample-tag">${this.sampleSize} data points evaluated</span>`
163
+ : ''}
164
+ </div>
165
+
166
+ <h3 class="title">Shift "${this.activityTitle}" schedule</h3>
167
+ <p class="explanation">${this.reason}</p>
168
+
169
+ ${this.suggestedTime
170
+ ? html `
171
+ <div class="time-pod">
172
+ <span class="time-label">Recommended Time</span>
173
+ <span class="time-val">${this.suggestedTime}</span>
174
+ </div>
175
+ `
176
+ : ''}
177
+
178
+ ${this.generatedAt
179
+ ? html `
180
+ <div class="footer">
181
+ <span>Model: Activityual Circadian v2</span>
182
+ <span>Updated: ${new Date(this.generatedAt).toLocaleDateString()}</span>
183
+ </div>
184
+ `
185
+ : ''}
186
+ </div>
187
+ `;
188
+ }
189
+ }
190
+ if (!customElements.get('activityual-recommendation-banner')) {
191
+ customElements.define('activityual-recommendation-banner', ActivityualRecommendationBanner);
192
+ }
@@ -0,0 +1,27 @@
1
+ import { LitElement } from 'lit';
2
+ export declare class ActivityualStatCard extends LitElement {
3
+ static properties: {
4
+ title: {
5
+ type: StringConstructor;
6
+ };
7
+ value: {
8
+ type: StringConstructor;
9
+ };
10
+ subtitle: {
11
+ type: StringConstructor;
12
+ };
13
+ delta: {
14
+ type: StringConstructor;
15
+ };
16
+ deltaType: {
17
+ type: StringConstructor;
18
+ };
19
+ };
20
+ title: string;
21
+ value: string;
22
+ subtitle: string;
23
+ delta: string;
24
+ deltaType: 'positive' | 'negative' | 'neutral';
25
+ static styles: import("lit").CSSResult;
26
+ render(): import("lit-html").TemplateResult<1>;
27
+ }
@@ -0,0 +1,137 @@
1
+ import { LitElement, html, css } from 'lit';
2
+ export class ActivityualStatCard extends LitElement {
3
+ constructor() {
4
+ super(...arguments);
5
+ this.title = '';
6
+ this.value = '0';
7
+ this.subtitle = '';
8
+ this.delta = '';
9
+ this.deltaType = 'neutral';
10
+ }
11
+ static { this.properties = {
12
+ title: { type: String },
13
+ value: { type: String },
14
+ subtitle: { type: String },
15
+ delta: { type: String },
16
+ deltaType: { type: String }
17
+ }; }
18
+ static { this.styles = css `
19
+ :host {
20
+ display: block;
21
+ box-sizing: border-box;
22
+ }
23
+
24
+ .stat-card {
25
+ background: linear-gradient(145deg, rgba(42, 42, 42, 0.65) 0%, rgba(24, 24, 24, 0.8) 100%);
26
+ backdrop-filter: blur(20px);
27
+ -webkit-backdrop-filter: blur(20px);
28
+ border: 1px solid rgba(255, 255, 255, 0.05);
29
+ border-radius: 16px;
30
+ padding: 1.15rem 1.35rem;
31
+ box-shadow:
32
+ 0 10px 28px -4px rgba(0, 0, 0, 0.45),
33
+ inset 0 1px 0 rgba(255, 255, 255, 0.08);
34
+ font-family: Inter, 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
35
+ display: flex;
36
+ flex-direction: column;
37
+ gap: 0.45rem;
38
+ box-sizing: border-box;
39
+ transition: all 0.2s ease;
40
+ }
41
+
42
+ .stat-card:hover {
43
+ border-color: rgba(255, 255, 255, 0.1);
44
+ background: linear-gradient(145deg, rgba(48, 48, 48, 0.75) 0%, rgba(28, 28, 28, 0.88) 100%);
45
+ transform: translateY(-1px);
46
+ }
47
+
48
+ .header {
49
+ display: flex;
50
+ align-items: center;
51
+ justify-content: space-between;
52
+ }
53
+
54
+ .title {
55
+ font-size: 11px;
56
+ font-weight: 600;
57
+ text-transform: uppercase;
58
+ letter-spacing: 0.05em;
59
+ color: #a5a5a5;
60
+ font-family: 'JetBrains Mono', monospace;
61
+ }
62
+
63
+ .value-row {
64
+ display: flex;
65
+ align-items: baseline;
66
+ gap: 0.65rem;
67
+ }
68
+
69
+ .value {
70
+ font-size: 28px;
71
+ font-weight: 700;
72
+ letter-spacing: -0.03em;
73
+ color: #f5f5f5;
74
+ line-height: 1.1;
75
+ font-family: 'JetBrains Mono', monospace;
76
+ font-variant-numeric: tabular-nums;
77
+ }
78
+
79
+ .delta-pill {
80
+ font-size: 10.5px;
81
+ font-weight: 600;
82
+ padding: 0.2rem 0.55rem;
83
+ border-radius: 9999px;
84
+ line-height: 1;
85
+ font-family: 'JetBrains Mono', monospace;
86
+ }
87
+
88
+ .delta-positive {
89
+ background: rgba(37, 211, 102, 0.12);
90
+ color: #25d366;
91
+ border: 1px solid rgba(37, 211, 102, 0.25);
92
+ }
93
+
94
+ .delta-negative {
95
+ background: rgba(255, 52, 52, 0.12);
96
+ color: #ff3434;
97
+ border: 1px solid rgba(255, 52, 52, 0.25);
98
+ }
99
+
100
+ .delta-neutral {
101
+ background: rgba(255, 255, 255, 0.06);
102
+ color: #a5a5a5;
103
+ border: 1px solid rgba(255, 255, 255, 0.08);
104
+ }
105
+
106
+ .subtitle {
107
+ font-size: 11.5px;
108
+ font-weight: 400;
109
+ color: #707070;
110
+ margin: 0;
111
+ }
112
+ `; }
113
+ render() {
114
+ return html `
115
+ <div class="stat-card">
116
+ <div class="header">
117
+ <span class="title">${this.title}</span>
118
+ <slot name="icon"></slot>
119
+ </div>
120
+ <div class="value-row">
121
+ <span class="value">${this.value}</span>
122
+ ${this.delta
123
+ ? html `
124
+ <span class="delta-pill delta-${this.deltaType}">
125
+ ${this.delta}
126
+ </span>
127
+ `
128
+ : ''}
129
+ </div>
130
+ ${this.subtitle ? html `<p class="subtitle">${this.subtitle}</p>` : ''}
131
+ </div>
132
+ `;
133
+ }
134
+ }
135
+ if (!customElements.get('activityual-stat-card')) {
136
+ customElements.define('activityual-stat-card', ActivityualStatCard);
137
+ }