@hotelcard/ui 0.0.2
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 +410 -0
- package/dist/index.css +362 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +166 -0
- package/dist/index.d.ts +166 -0
- package/dist/index.js +343 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +309 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
# @hotelcard/ui
|
|
2
|
+
|
|
3
|
+
Shared UI component library for HotelCard applications (mobile app and website).
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# In your project
|
|
11
|
+
npm install @hotelcard/ui
|
|
12
|
+
|
|
13
|
+
# Or with local development (file: protocol)
|
|
14
|
+
"@hotelcard/ui": "file:../hotelcard-monorepo/packages/ui"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Import Components
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { Button, Badge, Rating } from '@hotelcard/ui';
|
|
21
|
+
import type { ButtonProps, BadgeProps } from '@hotelcard/ui';
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Import Styles (REQUIRED)
|
|
25
|
+
|
|
26
|
+
You **must** import the CSS in your app's entry point (e.g., `main.tsx`):
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
// main.tsx or App.tsx
|
|
30
|
+
import '@hotelcard/ui/dist/index.css';
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**If you skip this step, components will render without styling!**
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Available Components
|
|
38
|
+
|
|
39
|
+
### Button
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { Button } from '@hotelcard/ui';
|
|
43
|
+
|
|
44
|
+
// Primary button (default)
|
|
45
|
+
<Button onClick={handleClick}>Submit</Button>
|
|
46
|
+
|
|
47
|
+
// Secondary button
|
|
48
|
+
<Button variant="secondary">Cancel</Button>
|
|
49
|
+
|
|
50
|
+
// Small size
|
|
51
|
+
<Button size="small">Small Button</Button>
|
|
52
|
+
|
|
53
|
+
// Link style
|
|
54
|
+
<Button variant="link">Learn More</Button>
|
|
55
|
+
|
|
56
|
+
// With icons
|
|
57
|
+
<Button leftIcon={<SearchIcon />}>Search</Button>
|
|
58
|
+
|
|
59
|
+
// Icon-only button
|
|
60
|
+
<Button iconOnly><HeartIcon /></Button>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Props:**
|
|
64
|
+
| Prop | Type | Default | Description |
|
|
65
|
+
|------|------|---------|-------------|
|
|
66
|
+
| `variant` | `'primary' \| 'secondary' \| 'link'` | `'primary'` | Button style |
|
|
67
|
+
| `size` | `'small' \| 'medium'` | `'medium'` | Button size |
|
|
68
|
+
| `iconOnly` | `boolean` | `false` | Icon-only circular button |
|
|
69
|
+
| `leftIcon` | `ReactNode` | - | Icon on the left |
|
|
70
|
+
| `rightIcon` | `ReactNode` | - | Icon on the right |
|
|
71
|
+
| `disabled` | `boolean` | `false` | Disabled state |
|
|
72
|
+
|
|
73
|
+
### Badge
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import { Badge } from '@hotelcard/ui';
|
|
77
|
+
|
|
78
|
+
// Discount badge
|
|
79
|
+
<Badge color="primary" size="small" style="heavy">
|
|
80
|
+
-50%
|
|
81
|
+
</Badge>
|
|
82
|
+
|
|
83
|
+
// Status badge
|
|
84
|
+
<Badge color="success" style="light">
|
|
85
|
+
Available
|
|
86
|
+
</Badge>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Props:**
|
|
90
|
+
| Prop | Type | Default | Description |
|
|
91
|
+
|------|------|---------|-------------|
|
|
92
|
+
| `color` | `'primary' \| 'secondary' \| 'neutral' \| 'success' \| 'warning'` | `'primary'` | Badge color |
|
|
93
|
+
| `size` | `'small' \| 'large'` | `'large'` | Badge size |
|
|
94
|
+
| `style` | `'heavy' \| 'light'` | `'heavy'` | Solid or light background |
|
|
95
|
+
|
|
96
|
+
### Rating
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
import { Rating } from '@hotelcard/ui';
|
|
100
|
+
|
|
101
|
+
// Star rating
|
|
102
|
+
<Rating value={4.5} />
|
|
103
|
+
|
|
104
|
+
// Show numeric value
|
|
105
|
+
<Rating value={4.5} showValue />
|
|
106
|
+
|
|
107
|
+
// Different sizes
|
|
108
|
+
<Rating value={3} size="small" />
|
|
109
|
+
<Rating value={3} size="large" />
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Props:**
|
|
113
|
+
| Prop | Type | Default | Description |
|
|
114
|
+
|------|------|---------|-------------|
|
|
115
|
+
| `value` | `number` | - | Rating value (0-5) |
|
|
116
|
+
| `maxValue` | `number` | `5` | Maximum stars |
|
|
117
|
+
| `showValue` | `boolean` | `false` | Show numeric value |
|
|
118
|
+
| `size` | `'small' \| 'medium' \| 'large'` | `'medium'` | Star size |
|
|
119
|
+
|
|
120
|
+
### Icons
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
import { HeartIcon, StarIcon, ChevronLeftIcon, ChevronRightIcon, PinIcon } from '@hotelcard/ui';
|
|
124
|
+
|
|
125
|
+
<HeartIcon filled={false} size={24} />
|
|
126
|
+
<StarIcon size={9} />
|
|
127
|
+
<ChevronLeftIcon size={20} />
|
|
128
|
+
<ChevronRightIcon size={20} />
|
|
129
|
+
<PinIcon size={16} />
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## CSS Variables (Theming)
|
|
135
|
+
|
|
136
|
+
Components use CSS variables for theming. Your app should define these in a global CSS file:
|
|
137
|
+
|
|
138
|
+
```css
|
|
139
|
+
:root {
|
|
140
|
+
/* Colors */
|
|
141
|
+
--background-action-primary-idle: #C81E4C;
|
|
142
|
+
--background-action-primary-hover: #a3183d;
|
|
143
|
+
--content-action-primary-inverse-idle: #ffffff;
|
|
144
|
+
--content-general-primary: #1F2937;
|
|
145
|
+
--content-general-secondary: #6B7280;
|
|
146
|
+
--border-general-divider: #D1D5DB;
|
|
147
|
+
|
|
148
|
+
/* Typography */
|
|
149
|
+
--font-primary: system-ui, -apple-system, sans-serif;
|
|
150
|
+
--font-weight-medium: 500;
|
|
151
|
+
--font-weight-semibold: 600;
|
|
152
|
+
|
|
153
|
+
/* Spacing & Sizing */
|
|
154
|
+
--radius-lg: 12px;
|
|
155
|
+
--radius-full: 9999px;
|
|
156
|
+
|
|
157
|
+
/* ... see tokens.css for full list */
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Components have fallback values, so they work without custom CSS variables.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Types
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
import type {
|
|
169
|
+
ButtonProps,
|
|
170
|
+
BadgeProps,
|
|
171
|
+
RatingProps,
|
|
172
|
+
} from '@hotelcard/ui';
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Development Guide
|
|
178
|
+
|
|
179
|
+
### Project Structure
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
packages/ui/
|
|
183
|
+
├── src/
|
|
184
|
+
│ ├── components/
|
|
185
|
+
│ │ ├── Button/
|
|
186
|
+
│ │ │ ├── Button.tsx
|
|
187
|
+
│ │ │ ├── Button.css # Prefixed classes (hc-btn-*)
|
|
188
|
+
│ │ │ ├── Button.types.ts
|
|
189
|
+
│ │ │ └── index.ts
|
|
190
|
+
│ │ ├── Badge/
|
|
191
|
+
│ │ ├── Rating/
|
|
192
|
+
│ │ └── icons/
|
|
193
|
+
│ └── index.ts # Main exports
|
|
194
|
+
├── dist/ # Built output
|
|
195
|
+
├── package.json
|
|
196
|
+
└── tsup.config.ts
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Adding a New Component
|
|
200
|
+
|
|
201
|
+
1. **Create component folder:**
|
|
202
|
+
```
|
|
203
|
+
src/components/NewComponent/
|
|
204
|
+
├── NewComponent.tsx
|
|
205
|
+
├── NewComponent.css
|
|
206
|
+
├── NewComponent.types.ts
|
|
207
|
+
└── index.ts
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
2. **Use prefixed CSS classes** (NOT CSS modules):
|
|
211
|
+
```tsx
|
|
212
|
+
// NewComponent.tsx
|
|
213
|
+
import './NewComponent.css';
|
|
214
|
+
|
|
215
|
+
const cx = (className: string) => `hc-newcomp-${className}`;
|
|
216
|
+
|
|
217
|
+
export const NewComponent = () => (
|
|
218
|
+
<div className={cx('container')}>
|
|
219
|
+
<span className={cx('text')}>Hello</span>
|
|
220
|
+
</div>
|
|
221
|
+
);
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
3. **Define CSS with prefixes:**
|
|
225
|
+
```css
|
|
226
|
+
/* NewComponent.css */
|
|
227
|
+
.hc-newcomp-container {
|
|
228
|
+
display: flex;
|
|
229
|
+
padding: var(--size-rem-1, 16px);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.hc-newcomp-text {
|
|
233
|
+
color: var(--content-general-primary, #1F2937);
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
4. **Export from index.ts:**
|
|
238
|
+
```tsx
|
|
239
|
+
// src/index.ts
|
|
240
|
+
export { NewComponent } from './components/NewComponent';
|
|
241
|
+
export type { NewComponentProps } from './components/NewComponent';
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
5. **Build and test:**
|
|
245
|
+
```bash
|
|
246
|
+
pnpm build
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### CSS Class Naming Convention
|
|
250
|
+
|
|
251
|
+
**Why prefixed classes instead of CSS Modules?**
|
|
252
|
+
|
|
253
|
+
CSS Modules don't work well with tsup bundling - the class name mappings become empty objects. We use prefixed classes instead:
|
|
254
|
+
|
|
255
|
+
- `hc-btn-*` for Button
|
|
256
|
+
- `hc-badge-*` for Badge
|
|
257
|
+
- `hc-rating-*` for Rating
|
|
258
|
+
|
|
259
|
+
This prevents conflicts with consuming app's styles while ensuring styles are always applied.
|
|
260
|
+
|
|
261
|
+
### Building
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
cd packages/ui
|
|
265
|
+
|
|
266
|
+
# Build once
|
|
267
|
+
pnpm build
|
|
268
|
+
|
|
269
|
+
# Watch mode for development
|
|
270
|
+
pnpm dev
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Testing Changes Locally
|
|
274
|
+
|
|
275
|
+
1. Build the package: `pnpm build`
|
|
276
|
+
2. In consuming app, reinstall: `npm install`
|
|
277
|
+
3. Restart dev server if needed
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Publishing to NPM
|
|
282
|
+
|
|
283
|
+
### Prerequisites
|
|
284
|
+
|
|
285
|
+
1. NPM account with access to `@hotelcard` organization
|
|
286
|
+
2. Logged in: `npm login`
|
|
287
|
+
|
|
288
|
+
### Publish Process
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
cd packages/ui
|
|
292
|
+
|
|
293
|
+
# 1. Make sure everything builds
|
|
294
|
+
pnpm build
|
|
295
|
+
|
|
296
|
+
# 2. Update version
|
|
297
|
+
npm version patch # 0.0.2 -> 0.0.3
|
|
298
|
+
# or
|
|
299
|
+
npm version minor # 0.0.2 -> 0.1.0
|
|
300
|
+
# or
|
|
301
|
+
npm version major # 0.0.2 -> 1.0.0
|
|
302
|
+
|
|
303
|
+
# 3. Publish
|
|
304
|
+
npm publish --access public
|
|
305
|
+
|
|
306
|
+
# 4. Commit version bump
|
|
307
|
+
git add package.json
|
|
308
|
+
git commit -m "chore(ui): release v0.0.3"
|
|
309
|
+
git push
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Version Guidelines
|
|
313
|
+
|
|
314
|
+
- `patch` (0.0.x): Bug fixes, style tweaks
|
|
315
|
+
- `minor` (0.x.0): New components, new features
|
|
316
|
+
- `major` (x.0.0): Breaking changes (API changes, removed props)
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Using in Apps
|
|
321
|
+
|
|
322
|
+
### hotelcard-app-v2 (Mobile App)
|
|
323
|
+
|
|
324
|
+
```tsx
|
|
325
|
+
// src/main.tsx
|
|
326
|
+
import '@hotelcard/ui/dist/index.css'; // REQUIRED
|
|
327
|
+
|
|
328
|
+
// src/library/index.ts (barrel export)
|
|
329
|
+
export { Button, Badge } from '@hotelcard/ui';
|
|
330
|
+
export type { ButtonProps, BadgeProps } from '@hotelcard/ui';
|
|
331
|
+
|
|
332
|
+
// src/pages/SomePage.tsx
|
|
333
|
+
import { Button, Badge } from '@/library';
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### hotelcard-ui (Website)
|
|
337
|
+
|
|
338
|
+
```tsx
|
|
339
|
+
// src/main.tsx or app entry
|
|
340
|
+
import '@hotelcard/ui/dist/index.css'; // REQUIRED
|
|
341
|
+
|
|
342
|
+
// Use directly
|
|
343
|
+
import { Button, Badge } from '@hotelcard/ui';
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Updating the Package
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
# After a new version is published
|
|
350
|
+
npm update @hotelcard/ui
|
|
351
|
+
|
|
352
|
+
# Or for local development
|
|
353
|
+
rm -rf node_modules/.cache
|
|
354
|
+
npm install
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## Troubleshooting
|
|
360
|
+
|
|
361
|
+
### "Components render without styles"
|
|
362
|
+
|
|
363
|
+
Make sure you imported the CSS:
|
|
364
|
+
```tsx
|
|
365
|
+
import '@hotelcard/ui/dist/index.css';
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### "Module not found" errors
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
# Clear cache and reinstall
|
|
372
|
+
rm -rf node_modules/.cache
|
|
373
|
+
npm install
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### "Types not found"
|
|
377
|
+
|
|
378
|
+
Make sure you're importing types with `type`:
|
|
379
|
+
```tsx
|
|
380
|
+
import type { ButtonProps } from '@hotelcard/ui';
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Local changes not reflecting
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
# In @hotelcard/ui
|
|
387
|
+
pnpm build
|
|
388
|
+
|
|
389
|
+
# In consuming app
|
|
390
|
+
npm install
|
|
391
|
+
# Restart dev server
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## Component Checklist
|
|
397
|
+
|
|
398
|
+
When adding/modifying components:
|
|
399
|
+
|
|
400
|
+
- [ ] Uses prefixed CSS classes (not CSS modules)
|
|
401
|
+
- [ ] All CSS variables have fallback values
|
|
402
|
+
- [ ] Props are typed in `.types.ts`
|
|
403
|
+
- [ ] Exported from `src/index.ts`
|
|
404
|
+
- [ ] Build passes: `pnpm build`
|
|
405
|
+
- [ ] Tested in consuming app
|
|
406
|
+
- [ ] README updated if new component
|
|
407
|
+
|
|
408
|
+
## License
|
|
409
|
+
|
|
410
|
+
MIT
|