@umbra.ui/core 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.
- package/dist/components/controls/Dropdown/types.d.ts +5 -0
- package/dist/components/controls/Dropdown/types.d.ts.map +1 -0
- package/dist/components/controls/Dropdown/types.js +1 -0
- package/dist/components/controls/SegmentedControl/types.d.ts +6 -0
- package/dist/components/controls/SegmentedControl/types.d.ts.map +1 -0
- package/dist/components/controls/SegmentedControl/types.js +1 -0
- package/dist/components/dialogs/Alert/types.d.ts +7 -0
- package/dist/components/dialogs/Alert/types.d.ts.map +1 -0
- package/dist/components/dialogs/Alert/types.js +1 -0
- package/dist/components/dialogs/Toast/types.d.ts +34 -0
- package/dist/components/dialogs/Toast/types.d.ts.map +1 -0
- package/dist/components/dialogs/Toast/types.js +10 -0
- package/dist/components/dialogs/Toast/useToast.d.ts +36 -0
- package/dist/components/dialogs/Toast/useToast.d.ts.map +1 -0
- package/dist/components/dialogs/Toast/useToast.js +90 -0
- package/dist/components/indicators/Tooltip/tooltip.d.ts +3 -0
- package/dist/components/indicators/Tooltip/tooltip.d.ts.map +1 -0
- package/dist/components/indicators/Tooltip/tooltip.js +33 -0
- package/dist/components/indicators/Tooltip/types.d.ts +14 -0
- package/dist/components/indicators/Tooltip/types.d.ts.map +1 -0
- package/dist/components/indicators/Tooltip/types.js +1 -0
- package/dist/components/indicators/Tooltip/useTooltip.d.ts +18 -0
- package/dist/components/indicators/Tooltip/useTooltip.d.ts.map +1 -0
- package/dist/components/indicators/Tooltip/useTooltip.js +57 -0
- package/dist/components/inputs/Tags/tag-bar-styles.d.ts +14 -0
- package/dist/components/inputs/Tags/tag-bar-styles.d.ts.map +1 -0
- package/dist/components/inputs/Tags/tag-bar-styles.js +313 -0
- package/dist/components/inputs/Tags/types.d.ts +93 -0
- package/dist/components/inputs/Tags/types.d.ts.map +1 -0
- package/dist/components/inputs/Tags/types.js +216 -0
- package/dist/components/inputs/search/types.d.ts +9 -0
- package/dist/components/inputs/search/types.d.ts.map +1 -0
- package/dist/components/inputs/search/types.js +1 -0
- package/dist/components/navigation/adaptive/types.d.ts +16 -0
- package/dist/components/navigation/adaptive/types.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/types.js +1 -0
- package/dist/components/navigation/adaptive/useAdaptiveLayout.d.ts +27 -0
- package/dist/components/navigation/adaptive/useAdaptiveLayout.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/useAdaptiveLayout.js +40 -0
- package/dist/components/navigation/adaptive/useBreakpoints.d.ts +6 -0
- package/dist/components/navigation/adaptive/useBreakpoints.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/useBreakpoints.js +37 -0
- package/dist/components/navigation/adaptive/useContainerMonitor.d.ts +93 -0
- package/dist/components/navigation/adaptive/useContainerMonitor.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/useContainerMonitor.js +145 -0
- package/dist/components/navigation/adaptive/useViewAnimation.d.ts +31 -0
- package/dist/components/navigation/adaptive/useViewAnimation.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/useViewAnimation.js +591 -0
- package/dist/components/navigation/adaptive/useViewResize.d.ts +52 -0
- package/dist/components/navigation/adaptive/useViewResize.d.ts.map +1 -0
- package/dist/components/navigation/adaptive/useViewResize.js +146 -0
- package/dist/components/navigation/navstack/useNavigationStack.d.ts +25 -0
- package/dist/components/navigation/navstack/useNavigationStack.d.ts.map +1 -0
- package/dist/components/navigation/navstack/useNavigationStack.js +133 -0
- package/dist/components/navigation/slideover/useSlideoverController.d.ts +20 -0
- package/dist/components/navigation/slideover/useSlideoverController.d.ts.map +1 -0
- package/dist/components/navigation/slideover/useSlideoverController.js +267 -0
- package/dist/components/navigation/splitview/useSplitViewController.d.ts +20 -0
- package/dist/components/navigation/splitview/useSplitViewController.d.ts.map +1 -0
- package/dist/components/navigation/splitview/useSplitViewController.js +325 -0
- package/dist/components/navigation/tabcontroller/types.d.ts +21 -0
- package/dist/components/navigation/tabcontroller/types.d.ts.map +1 -0
- package/dist/components/navigation/tabcontroller/types.js +1 -0
- package/dist/components/navigation/tabcontroller/useTabController.d.ts +5 -0
- package/dist/components/navigation/tabcontroller/useTabController.d.ts.map +1 -0
- package/dist/components/navigation/tabcontroller/useTabController.js +10 -0
- package/dist/components/navigation/types.d.ts +8 -0
- package/dist/components/navigation/types.d.ts.map +1 -0
- package/dist/components/navigation/types.js +1 -0
- package/dist/components/pickers/CollectionPicker/types.d.ts +11 -0
- package/dist/components/pickers/CollectionPicker/types.d.ts.map +1 -0
- package/dist/components/pickers/CollectionPicker/types.js +1 -0
- package/dist/components/pickers/ColorPicker/colors.d.ts +13 -0
- package/dist/components/pickers/ColorPicker/colors.d.ts.map +1 -0
- package/dist/components/pickers/ColorPicker/colors.js +266 -0
- package/dist/components/pickers/FilePicker/types.d.ts +10 -0
- package/dist/components/pickers/FilePicker/types.d.ts.map +1 -0
- package/dist/components/pickers/FilePicker/types.js +1 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +196 -0
- package/dist/theme.d.ts +73 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +279 -0
- package/dist/themes/blank.d.ts +7 -0
- package/dist/themes/blank.d.ts.map +1 -0
- package/dist/themes/blank.js +543 -0
- package/dist/themes/crimson-dark.d.ts +4 -0
- package/dist/themes/crimson-dark.d.ts.map +1 -0
- package/dist/themes/crimson-dark.js +552 -0
- package/dist/themes/cyan-light.d.ts +4 -0
- package/dist/themes/cyan-light.d.ts.map +1 -0
- package/dist/themes/cyan-light.js +552 -0
- package/dist/themes/dark.d.ts +4 -0
- package/dist/themes/dark.d.ts.map +1 -0
- package/dist/themes/dark.js +551 -0
- package/dist/themes/gold-dark.d.ts +4 -0
- package/dist/themes/gold-dark.d.ts.map +1 -0
- package/dist/themes/gold-dark.js +552 -0
- package/dist/themes/grass-dark.d.ts +4 -0
- package/dist/themes/grass-dark.d.ts.map +1 -0
- package/dist/themes/grass-dark.js +552 -0
- package/dist/themes/indigo.d.ts +4 -0
- package/dist/themes/indigo.d.ts.map +1 -0
- package/dist/themes/indigo.js +552 -0
- package/dist/themes/light.d.ts +4 -0
- package/dist/themes/light.d.ts.map +1 -0
- package/dist/themes/light.js +551 -0
- package/dist/themes/orange-dark.d.ts +4 -0
- package/dist/themes/orange-dark.d.ts.map +1 -0
- package/dist/themes/orange-dark.js +551 -0
- package/dist/themes/orange-light.d.ts +4 -0
- package/dist/themes/orange-light.d.ts.map +1 -0
- package/dist/themes/orange-light.js +551 -0
- package/package.json +62 -0
- package/src/components/controls/Button/Button.vue +417 -0
- package/src/components/controls/Button/README.md +348 -0
- package/src/components/controls/Button/theme.css +200 -0
- package/src/components/controls/Checkbox/Checkbox.vue +164 -0
- package/src/components/controls/Checkbox/README.md +441 -0
- package/src/components/controls/Checkbox/theme.css +36 -0
- package/src/components/controls/Dropdown/Dropdown.vue +476 -0
- package/src/components/controls/Dropdown/README.md +370 -0
- package/src/components/controls/Dropdown/theme.css +50 -0
- package/src/components/controls/Dropdown/types.ts +6 -0
- package/src/components/controls/IconButton/IconButton.vue +267 -0
- package/src/components/controls/IconButton/README.md +502 -0
- package/src/components/controls/IconButton/theme.css +89 -0
- package/src/components/controls/Radio/README.md +591 -0
- package/src/components/controls/Radio/Radio.vue +89 -0
- package/src/components/controls/Radio/theme.css +14 -0
- package/src/components/controls/RangeSlider/README.md +608 -0
- package/src/components/controls/RangeSlider/RangeSlider.vue +535 -0
- package/src/components/controls/RangeSlider/theme.css +80 -0
- package/src/components/controls/SegmentedControl/README.md +587 -0
- package/src/components/controls/SegmentedControl/SegmentedControl.vue +284 -0
- package/src/components/controls/SegmentedControl/theme.css +60 -0
- package/src/components/controls/SegmentedControl/types.ts +5 -0
- package/src/components/controls/Slider/README.md +627 -0
- package/src/components/controls/Slider/Slider.vue +260 -0
- package/src/components/controls/Slider/theme.css +74 -0
- package/src/components/controls/Stepper/README.md +601 -0
- package/src/components/controls/Stepper/Stepper.vue +103 -0
- package/src/components/controls/Stepper/theme.css +53 -0
- package/src/components/controls/Switch/README.md +667 -0
- package/src/components/controls/Switch/Switch.vue +127 -0
- package/src/components/controls/Switch/theme.css +42 -0
- package/src/components/dialogs/Alert/Alert.vue +218 -0
- package/src/components/dialogs/Alert/README.md +450 -0
- package/src/components/dialogs/Alert/theme.css +44 -0
- package/src/components/dialogs/Alert/types.ts +11 -0
- package/src/components/dialogs/Toast/README.md +522 -0
- package/src/components/dialogs/Toast/Toast.vue +296 -0
- package/src/components/dialogs/Toast/ToastContainer.vue +330 -0
- package/src/components/dialogs/Toast/theme.css +44 -0
- package/src/components/dialogs/Toast/types.ts +46 -0
- package/src/components/dialogs/Toast/useToast.ts +127 -0
- package/src/components/indicators/ProgressBar/ProgressBar.vue +98 -0
- package/src/components/indicators/ProgressBar/README.md +744 -0
- package/src/components/indicators/ProgressBar/theme.css +36 -0
- package/src/components/indicators/Tooltip/README.md +723 -0
- package/src/components/indicators/Tooltip/TooltipProvider.vue +142 -0
- package/src/components/indicators/Tooltip/theme.css +18 -0
- package/src/components/indicators/Tooltip/tooltip.ts +48 -0
- package/src/components/indicators/Tooltip/types.ts +15 -0
- package/src/components/indicators/Tooltip/useTooltip.ts +71 -0
- package/src/components/inputs/AutogrowTextView/AutogrowTextView.vue +110 -0
- package/src/components/inputs/AutogrowTextView/README.md +643 -0
- package/src/components/inputs/AutogrowTextView/theme.css +28 -0
- package/src/components/inputs/InputCard/InputCard.vue +600 -0
- package/src/components/inputs/InputCard/README.md +636 -0
- package/src/components/inputs/InputEmail/InputEmail.vue +698 -0
- package/src/components/inputs/InputEmail/README.md +764 -0
- package/src/components/inputs/InputNumber/InputNumber.vue +300 -0
- package/src/components/inputs/InputNumber/README.md +749 -0
- package/src/components/inputs/InputPhone/InputPhone.vue +645 -0
- package/src/components/inputs/InputPhone/README.md +636 -0
- package/src/components/inputs/InputSecure/InputSecure.vue +646 -0
- package/src/components/inputs/InputSecure/README.md +771 -0
- package/src/components/inputs/InputText/InputText.vue +225 -0
- package/src/components/inputs/InputText/README.md +844 -0
- package/src/components/inputs/OTP/OTP.vue +349 -0
- package/src/components/inputs/OTP/README.md +736 -0
- package/src/components/inputs/OTP/theme.css +50 -0
- package/src/components/inputs/StringCapture/README.md +718 -0
- package/src/components/inputs/StringCapture/StringCapture.vue +315 -0
- package/src/components/inputs/StringCapture/theme.css +86 -0
- package/src/components/inputs/Tags/README.md +897 -0
- package/src/components/inputs/Tags/TagBar.vue +793 -0
- package/src/components/inputs/Tags/TagCreation.vue +219 -0
- package/src/components/inputs/Tags/TagPicker.vue +380 -0
- package/src/components/inputs/Tags/tag-bar-styles.ts +354 -0
- package/src/components/inputs/Tags/theme.css +121 -0
- package/src/components/inputs/Tags/types.ts +346 -0
- package/src/components/inputs/search/README.md +759 -0
- package/src/components/inputs/search/SearchBar.vue +394 -0
- package/src/components/inputs/search/SearchResults.vue +310 -0
- package/src/components/inputs/search/theme.css +187 -0
- package/src/components/inputs/search/types.ts +8 -0
- package/src/components/inputs/theme.css +102 -0
- package/src/components/menus/ActionMenu/ActionMenu.vue +383 -0
- package/src/components/menus/ActionMenu/README.md +825 -0
- package/src/components/menus/ActionMenu/theme.css +93 -0
- package/src/components/models/Popover/Popover.vue +551 -0
- package/src/components/models/Popover/README.md +885 -0
- package/src/components/models/Popover/theme.css +52 -0
- package/src/components/models/Sheet/README.md +1159 -0
- package/src/components/models/Sheet/Sheet.vue +465 -0
- package/src/components/models/Sheet/theme.css +72 -0
- package/src/components/models/Sidebar/README.md +1228 -0
- package/src/components/models/Sidebar/Sidebar.vue +480 -0
- package/src/components/models/Sidebar/theme.css +90 -0
- package/src/components/navigation/adaptive/AdaptiveLayout.vue +779 -0
- package/src/components/navigation/adaptive/AdaptiveLayoutBreadcrumbs.vue +192 -0
- package/src/components/navigation/adaptive/AdaptiveLayoutMenuButton.vue +149 -0
- package/src/components/navigation/adaptive/README.md +768 -0
- package/src/components/navigation/adaptive/types.ts +19 -0
- package/src/components/navigation/adaptive/useAdaptiveLayout.ts +89 -0
- package/src/components/navigation/adaptive/useBreakpoints.ts +41 -0
- package/src/components/navigation/adaptive/useContainerMonitor.ts +214 -0
- package/src/components/navigation/adaptive/useViewAnimation.ts +721 -0
- package/src/components/navigation/adaptive/useViewResize.ts +211 -0
- package/src/components/navigation/navstack/NavigationStack.vue +180 -0
- package/src/components/navigation/navstack/README.md +994 -0
- package/src/components/navigation/navstack/useNavigationStack.ts +164 -0
- package/src/components/navigation/slideover/README.md +1275 -0
- package/src/components/navigation/slideover/SlideoverController.vue +287 -0
- package/src/components/navigation/slideover/useSlideoverController.ts +320 -0
- package/src/components/navigation/splitview/README.md +1115 -0
- package/src/components/navigation/splitview/SplitViewController.vue +176 -0
- package/src/components/navigation/splitview/useSplitViewController.ts +388 -0
- package/src/components/navigation/tabcontroller/README.md +919 -0
- package/src/components/navigation/tabcontroller/TabController.vue +307 -0
- package/src/components/navigation/tabcontroller/TabItem.vue +57 -0
- package/src/components/navigation/tabcontroller/types.ts +24 -0
- package/src/components/navigation/tabcontroller/useTabController.ts +18 -0
- package/src/components/navigation/theme.css +91 -0
- package/src/components/navigation/types.ts +7 -0
- package/src/components/pickers/CollectionPicker/CollectionPicker.vue +398 -0
- package/src/components/pickers/CollectionPicker/README.md +1115 -0
- package/src/components/pickers/CollectionPicker/theme.css +14 -0
- package/src/components/pickers/CollectionPicker/types.ts +11 -0
- package/src/components/pickers/ColorPicker/ColorPicker.vue +376 -0
- package/src/components/pickers/ColorPicker/README.md +1439 -0
- package/src/components/pickers/ColorPicker/colors.ts +299 -0
- package/src/components/pickers/ColorPicker/theme.css +32 -0
- package/src/components/pickers/DatePicker/DatePicker.vue +660 -0
- package/src/components/pickers/DatePicker/README.md +1195 -0
- package/src/components/pickers/DatePicker/theme.css +22 -0
- package/src/components/pickers/FilePicker/FilePicker.vue +534 -0
- package/src/components/pickers/FilePicker/README.md +1542 -0
- package/src/components/pickers/FilePicker/theme.css +48 -0
- package/src/components/pickers/FilePicker/types.ts +10 -0
- package/src/components/pickers/IconPicker/IconPicker.vue +327 -0
- package/src/components/pickers/IconPicker/README.md +1161 -0
- package/src/components/pickers/IconPicker/theme.css +28 -0
- package/src/components/pickers/theme.css +82 -0
- package/src/components/views/MarkdownViewer/MarkdownViewer.vue +442 -0
- package/src/components/views/MarkdownViewer/README.md +833 -0
- package/src/components/views/MarkdownViewer/theme.css +130 -0
- package/src/index.ts +263 -0
- package/src/theme.ts +378 -0
- package/src/themes/crimson-dark.ts +556 -0
- package/src/themes/cyan-light.ts +556 -0
- package/src/themes/dark.ts +557 -0
- package/src/themes/gold-dark.ts +556 -0
- package/src/themes/grass-dark.ts +556 -0
- package/src/themes/indigo.ts +556 -0
- package/src/themes/light.ts +557 -0
- package/src/themes/orange-dark.ts +557 -0
- package/src/themes/orange-light.ts +557 -0
- package/src/vue.d.ts +45 -0
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
# InputCard
|
|
2
|
+
|
|
3
|
+
A specialized credit card number input component built with Vue 3 Composition API and TypeScript. The InputCard component provides automatic formatting, card type detection, validation using the Luhn algorithm, and visual feedback with card brand icons.
|
|
4
|
+
|
|
5
|
+
## Installation/Import
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { InputCard } from "@umbra-ui/core";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Dependencies:**
|
|
12
|
+
|
|
13
|
+
- Vue 3.x
|
|
14
|
+
- @umbra-ui/icons (for card and validation icons)
|
|
15
|
+
|
|
16
|
+
## Basic Usage
|
|
17
|
+
|
|
18
|
+
```vue
|
|
19
|
+
<script setup lang="ts">
|
|
20
|
+
import { ref } from "vue";
|
|
21
|
+
import { InputCard } from "@umbra-ui/core";
|
|
22
|
+
|
|
23
|
+
const cardNumber = ref("");
|
|
24
|
+
const formattedCardNumber = ref("");
|
|
25
|
+
const cardType = ref("");
|
|
26
|
+
const isValid = ref(false);
|
|
27
|
+
|
|
28
|
+
const handleCardUpdate = (value: string) => {
|
|
29
|
+
console.log("Raw card number:", value);
|
|
30
|
+
cardNumber.value = value;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const handleFormattedUpdate = (formatted: string) => {
|
|
34
|
+
console.log("Formatted card number:", formatted);
|
|
35
|
+
formattedCardNumber.value = formatted;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const handleCardTypeUpdate = (type: string) => {
|
|
39
|
+
console.log("Card type:", type);
|
|
40
|
+
cardType.value = type;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const handleValidUpdate = (valid: boolean) => {
|
|
44
|
+
console.log("Is valid:", valid);
|
|
45
|
+
isValid.value = valid;
|
|
46
|
+
};
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<template>
|
|
50
|
+
<div>
|
|
51
|
+
<InputCard
|
|
52
|
+
v-model:value="cardNumber"
|
|
53
|
+
placeholder="4242 4242 4242 4242"
|
|
54
|
+
:showCardType="true"
|
|
55
|
+
@update:value="handleCardUpdate"
|
|
56
|
+
@update:formatted="handleFormattedUpdate"
|
|
57
|
+
@update:cardType="handleCardTypeUpdate"
|
|
58
|
+
@update:valid="handleValidUpdate"
|
|
59
|
+
/>
|
|
60
|
+
|
|
61
|
+
<div v-if="cardType" class="card-info">
|
|
62
|
+
<p>Card Type: {{ cardType }}</p>
|
|
63
|
+
<p>Valid: {{ isValid ? "Yes" : "No" }}</p>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</template>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Props
|
|
70
|
+
|
|
71
|
+
| Prop Name | Type | Required | Default | Description |
|
|
72
|
+
| -------------- | ------------------------------------------------------------- | -------- | ----------------------- | ------------------------------------------- |
|
|
73
|
+
| `value` | `string` | No | `""` | The raw card number (digits only) |
|
|
74
|
+
| `placeholder` | `string` | No | `"4242 4242 4242 4242"` | Placeholder text displayed when empty |
|
|
75
|
+
| `showCardType` | `boolean` | No | `true` | Whether to show the detected card type icon |
|
|
76
|
+
| `state` | `"normal" \| "active" \| "disabled" \| "readonly" \| "error"` | No | `"normal"` | Current state of the input field |
|
|
77
|
+
|
|
78
|
+
## Events
|
|
79
|
+
|
|
80
|
+
| Event Name | Payload Type | Description |
|
|
81
|
+
| ------------------ | ------------ | --------------------------------------------------------------------------------- |
|
|
82
|
+
| `update:value` | `string` | Emitted when the card number changes, returns raw digits only |
|
|
83
|
+
| `update:formatted` | `string` | Emitted when the card number changes, returns formatted display value |
|
|
84
|
+
| `update:cardType` | `string` | Emitted when the card type is detected, returns card brand name |
|
|
85
|
+
| `update:valid` | `boolean` | Emitted when validation state changes, returns true if card is complete and valid |
|
|
86
|
+
|
|
87
|
+
## Slots
|
|
88
|
+
|
|
89
|
+
This component does not use slots.
|
|
90
|
+
|
|
91
|
+
## Exposed Methods/Refs
|
|
92
|
+
|
|
93
|
+
This component does not expose any methods or refs.
|
|
94
|
+
|
|
95
|
+
## Supported Card Types
|
|
96
|
+
|
|
97
|
+
The component automatically detects and supports the following card types:
|
|
98
|
+
|
|
99
|
+
- **Visa**: 4xxx (16 or 19 digits)
|
|
100
|
+
- **Mastercard**: 5[1-5]xxx or 2[2-7]xxx (16 digits)
|
|
101
|
+
- **American Express**: 3[47]xxx (15 digits)
|
|
102
|
+
- **Discover**: 6011xxx, 65xxx, or 64[4-9]xxx (16 digits)
|
|
103
|
+
- **Diners Club**: 36xxx, 38xxx, or 30[0-5]xxx (14 digits)
|
|
104
|
+
- **JCB**: 35xxx (16 digits)
|
|
105
|
+
|
|
106
|
+
## CSS Customization
|
|
107
|
+
|
|
108
|
+
The InputCard component uses CSS custom properties for theming. It inherits from the general input theme:
|
|
109
|
+
|
|
110
|
+
```css
|
|
111
|
+
/* Input colors */
|
|
112
|
+
--input-background-normal: #ffffff;
|
|
113
|
+
--input-background-filled: #f8f9fa;
|
|
114
|
+
--input-text-empty: #6c757d;
|
|
115
|
+
--input-text-filled: #212529;
|
|
116
|
+
--input-border: #ced4da;
|
|
117
|
+
--input-border-filled: #80bdff;
|
|
118
|
+
--input-focus-border: #007bff;
|
|
119
|
+
--input-placeholder: #6c757d;
|
|
120
|
+
--input-placeholder-filled: #adb5bd;
|
|
121
|
+
|
|
122
|
+
/* State-specific colors */
|
|
123
|
+
--input-disabled-bg: #e9ecef;
|
|
124
|
+
--input-disabled-text: #6c757d;
|
|
125
|
+
--input-disabled-border: #ced4da;
|
|
126
|
+
--input-disabled-placeholder: #adb5bd;
|
|
127
|
+
|
|
128
|
+
--input-readonly-bg: #f8f9fa;
|
|
129
|
+
--input-readonly-text: #495057;
|
|
130
|
+
--input-readonly-border: #ced4da;
|
|
131
|
+
--input-readonly-placeholder: #6c757d;
|
|
132
|
+
|
|
133
|
+
--input-error-bg: #f8d7da;
|
|
134
|
+
--input-error-text: #721c24;
|
|
135
|
+
--input-error-border: #dc3545;
|
|
136
|
+
--input-text-error: #dc3545;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Examples
|
|
140
|
+
|
|
141
|
+
### Basic Card Input
|
|
142
|
+
|
|
143
|
+
```vue
|
|
144
|
+
<script setup lang="ts">
|
|
145
|
+
import { ref } from "vue";
|
|
146
|
+
import { InputCard } from "@umbra-ui/core";
|
|
147
|
+
|
|
148
|
+
const cardNumber = ref("");
|
|
149
|
+
</script>
|
|
150
|
+
|
|
151
|
+
<template>
|
|
152
|
+
<div class="card-form">
|
|
153
|
+
<h3>Payment Information</h3>
|
|
154
|
+
|
|
155
|
+
<div class="form-group">
|
|
156
|
+
<label>Card Number</label>
|
|
157
|
+
<InputCard
|
|
158
|
+
v-model:value="cardNumber"
|
|
159
|
+
placeholder="1234 5678 9012 3456"
|
|
160
|
+
:showCardType="true"
|
|
161
|
+
/>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</template>
|
|
165
|
+
|
|
166
|
+
<style module>
|
|
167
|
+
.card-form {
|
|
168
|
+
max-width: 400px;
|
|
169
|
+
padding: 1.5rem;
|
|
170
|
+
border: 1px solid #e0e0e0;
|
|
171
|
+
border-radius: 8px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.form-group {
|
|
175
|
+
margin-bottom: 1rem;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
label {
|
|
179
|
+
display: block;
|
|
180
|
+
margin-bottom: 0.5rem;
|
|
181
|
+
font-weight: 500;
|
|
182
|
+
color: #333;
|
|
183
|
+
}
|
|
184
|
+
</style>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Complete Payment Form
|
|
188
|
+
|
|
189
|
+
```vue
|
|
190
|
+
<script setup lang="ts">
|
|
191
|
+
import { ref, computed } from "vue";
|
|
192
|
+
import { InputCard } from "@umbra-ui/core";
|
|
193
|
+
|
|
194
|
+
const paymentData = ref({
|
|
195
|
+
cardNumber: "",
|
|
196
|
+
cardType: "",
|
|
197
|
+
isValid: false,
|
|
198
|
+
expiryMonth: "",
|
|
199
|
+
expiryYear: "",
|
|
200
|
+
cvv: "",
|
|
201
|
+
cardholderName: "",
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const canSubmit = computed(() => {
|
|
205
|
+
return (
|
|
206
|
+
paymentData.value.isValid &&
|
|
207
|
+
paymentData.value.expiryMonth &&
|
|
208
|
+
paymentData.value.expiryYear &&
|
|
209
|
+
paymentData.value.cvv &&
|
|
210
|
+
paymentData.value.cardholderName
|
|
211
|
+
);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const handleSubmit = () => {
|
|
215
|
+
if (canSubmit.value) {
|
|
216
|
+
console.log("Processing payment:", paymentData.value);
|
|
217
|
+
// Process payment logic here
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
</script>
|
|
221
|
+
|
|
222
|
+
<template>
|
|
223
|
+
<div class="payment-form">
|
|
224
|
+
<h3>Complete Payment</h3>
|
|
225
|
+
|
|
226
|
+
<form @submit.prevent="handleSubmit">
|
|
227
|
+
<div class="form-group">
|
|
228
|
+
<label>Card Number</label>
|
|
229
|
+
<InputCard
|
|
230
|
+
v-model:value="paymentData.cardNumber"
|
|
231
|
+
@update:cardType="paymentData.cardType = $event"
|
|
232
|
+
@update:valid="paymentData.isValid = $event"
|
|
233
|
+
placeholder="1234 5678 9012 3456"
|
|
234
|
+
/>
|
|
235
|
+
<p v-if="paymentData.cardType" class="card-type">
|
|
236
|
+
Detected: {{ paymentData.cardType }}
|
|
237
|
+
</p>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
<div class="form-row">
|
|
241
|
+
<div class="form-group">
|
|
242
|
+
<label>Expiry Month</label>
|
|
243
|
+
<select v-model="paymentData.expiryMonth" required>
|
|
244
|
+
<option value="">Month</option>
|
|
245
|
+
<option
|
|
246
|
+
v-for="month in 12"
|
|
247
|
+
:key="month"
|
|
248
|
+
:value="month.toString().padStart(2, '0')"
|
|
249
|
+
>
|
|
250
|
+
{{ month.toString().padStart(2, "0") }}
|
|
251
|
+
</option>
|
|
252
|
+
</select>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
<div class="form-group">
|
|
256
|
+
<label>Expiry Year</label>
|
|
257
|
+
<select v-model="paymentData.expiryYear" required>
|
|
258
|
+
<option value="">Year</option>
|
|
259
|
+
<option
|
|
260
|
+
v-for="year in 10"
|
|
261
|
+
:key="year"
|
|
262
|
+
:value="(new Date().getFullYear() + year).toString()"
|
|
263
|
+
>
|
|
264
|
+
{{ new Date().getFullYear() + year }}
|
|
265
|
+
</option>
|
|
266
|
+
</select>
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
<div class="form-group">
|
|
270
|
+
<label>CVV</label>
|
|
271
|
+
<input
|
|
272
|
+
v-model="paymentData.cvv"
|
|
273
|
+
type="text"
|
|
274
|
+
placeholder="123"
|
|
275
|
+
maxlength="4"
|
|
276
|
+
pattern="[0-9]*"
|
|
277
|
+
inputmode="numeric"
|
|
278
|
+
required
|
|
279
|
+
/>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
|
|
283
|
+
<div class="form-group">
|
|
284
|
+
<label>Cardholder Name</label>
|
|
285
|
+
<input
|
|
286
|
+
v-model="paymentData.cardholderName"
|
|
287
|
+
type="text"
|
|
288
|
+
placeholder="John Doe"
|
|
289
|
+
required
|
|
290
|
+
/>
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
<button type="submit" :disabled="!canSubmit">Process Payment</button>
|
|
294
|
+
</form>
|
|
295
|
+
</div>
|
|
296
|
+
</template>
|
|
297
|
+
|
|
298
|
+
<style module>
|
|
299
|
+
.payment-form {
|
|
300
|
+
max-width: 500px;
|
|
301
|
+
padding: 1.5rem;
|
|
302
|
+
border: 1px solid #e0e0e0;
|
|
303
|
+
border-radius: 8px;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.form-group {
|
|
307
|
+
margin-bottom: 1rem;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.form-row {
|
|
311
|
+
display: grid;
|
|
312
|
+
grid-template-columns: 1fr 1fr 1fr;
|
|
313
|
+
gap: 1rem;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
label {
|
|
317
|
+
display: block;
|
|
318
|
+
margin-bottom: 0.5rem;
|
|
319
|
+
font-weight: 500;
|
|
320
|
+
color: #333;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
input,
|
|
324
|
+
select {
|
|
325
|
+
width: 100%;
|
|
326
|
+
padding: 0.5rem;
|
|
327
|
+
border: 1px solid #ddd;
|
|
328
|
+
border-radius: 4px;
|
|
329
|
+
font-size: 1rem;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.card-type {
|
|
333
|
+
margin-top: 0.25rem;
|
|
334
|
+
font-size: 0.9rem;
|
|
335
|
+
color: #666;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
button {
|
|
339
|
+
width: 100%;
|
|
340
|
+
padding: 0.75rem;
|
|
341
|
+
background-color: #007bff;
|
|
342
|
+
color: white;
|
|
343
|
+
border: none;
|
|
344
|
+
border-radius: 4px;
|
|
345
|
+
cursor: pointer;
|
|
346
|
+
font-size: 1rem;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
button:disabled {
|
|
350
|
+
background-color: #ccc;
|
|
351
|
+
cursor: not-allowed;
|
|
352
|
+
}
|
|
353
|
+
</style>
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Different States
|
|
357
|
+
|
|
358
|
+
```vue
|
|
359
|
+
<script setup lang="ts">
|
|
360
|
+
import { ref } from "vue";
|
|
361
|
+
import { InputCard } from "@umbra-ui/core";
|
|
362
|
+
|
|
363
|
+
const normalCard = ref("");
|
|
364
|
+
const disabledCard = ref("4242424242424242");
|
|
365
|
+
const readonlyCard = ref("5555555555554444");
|
|
366
|
+
const errorCard = ref("1234567890123456"); // Invalid card
|
|
367
|
+
</script>
|
|
368
|
+
|
|
369
|
+
<template>
|
|
370
|
+
<div class="states-example">
|
|
371
|
+
<h3>Input States</h3>
|
|
372
|
+
|
|
373
|
+
<div class="state-group">
|
|
374
|
+
<label>Normal State</label>
|
|
375
|
+
<InputCard
|
|
376
|
+
v-model:value="normalCard"
|
|
377
|
+
placeholder="Enter card number"
|
|
378
|
+
state="normal"
|
|
379
|
+
/>
|
|
380
|
+
</div>
|
|
381
|
+
|
|
382
|
+
<div class="state-group">
|
|
383
|
+
<label>Disabled State</label>
|
|
384
|
+
<InputCard v-model:value="disabledCard" state="disabled" />
|
|
385
|
+
</div>
|
|
386
|
+
|
|
387
|
+
<div class="state-group">
|
|
388
|
+
<label>Readonly State</label>
|
|
389
|
+
<InputCard v-model:value="readonlyCard" state="readonly" />
|
|
390
|
+
</div>
|
|
391
|
+
|
|
392
|
+
<div class="state-group">
|
|
393
|
+
<label>Error State (Invalid Card)</label>
|
|
394
|
+
<InputCard v-model:value="errorCard" state="normal" />
|
|
395
|
+
</div>
|
|
396
|
+
</div>
|
|
397
|
+
</template>
|
|
398
|
+
|
|
399
|
+
<style module>
|
|
400
|
+
.states-example {
|
|
401
|
+
max-width: 500px;
|
|
402
|
+
padding: 1.5rem;
|
|
403
|
+
border: 1px solid #e0e0e0;
|
|
404
|
+
border-radius: 8px;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.state-group {
|
|
408
|
+
margin-bottom: 1.5rem;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
label {
|
|
412
|
+
display: block;
|
|
413
|
+
margin-bottom: 0.5rem;
|
|
414
|
+
font-weight: 500;
|
|
415
|
+
color: #333;
|
|
416
|
+
}
|
|
417
|
+
</style>
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Card Type Detection Demo
|
|
421
|
+
|
|
422
|
+
```vue
|
|
423
|
+
<script setup lang="ts">
|
|
424
|
+
import { ref } from "vue";
|
|
425
|
+
import { InputCard } from "@umbra-ui/core";
|
|
426
|
+
|
|
427
|
+
const testCards = ref([
|
|
428
|
+
{ name: "Visa", number: "4111111111111111" },
|
|
429
|
+
{ name: "Mastercard", number: "5555555555554444" },
|
|
430
|
+
{ name: "American Express", number: "378282246310005" },
|
|
431
|
+
{ name: "Discover", number: "6011111111111117" },
|
|
432
|
+
{ name: "Diners Club", number: "30569309025904" },
|
|
433
|
+
{ name: "JCB", number: "3530111333300000" },
|
|
434
|
+
]);
|
|
435
|
+
|
|
436
|
+
const currentCard = ref("");
|
|
437
|
+
const detectedType = ref("");
|
|
438
|
+
const isValid = ref(false);
|
|
439
|
+
|
|
440
|
+
const testCard = (cardNumber: string) => {
|
|
441
|
+
currentCard.value = cardNumber;
|
|
442
|
+
};
|
|
443
|
+
</script>
|
|
444
|
+
|
|
445
|
+
<template>
|
|
446
|
+
<div class="detection-demo">
|
|
447
|
+
<h3>Card Type Detection</h3>
|
|
448
|
+
|
|
449
|
+
<div class="demo-input">
|
|
450
|
+
<InputCard
|
|
451
|
+
v-model:value="currentCard"
|
|
452
|
+
@update:cardType="detectedType = $event"
|
|
453
|
+
@update:valid="isValid = $event"
|
|
454
|
+
placeholder="Enter or test a card number"
|
|
455
|
+
/>
|
|
456
|
+
|
|
457
|
+
<div v-if="detectedType || isValid !== null" class="detection-info">
|
|
458
|
+
<p><strong>Detected Type:</strong> {{ detectedType || "Unknown" }}</p>
|
|
459
|
+
<p><strong>Valid:</strong> {{ isValid ? "Yes" : "No" }}</p>
|
|
460
|
+
</div>
|
|
461
|
+
</div>
|
|
462
|
+
|
|
463
|
+
<div class="test-cards">
|
|
464
|
+
<h4>Test Cards</h4>
|
|
465
|
+
<div class="card-buttons">
|
|
466
|
+
<button
|
|
467
|
+
v-for="card in testCards"
|
|
468
|
+
:key="card.name"
|
|
469
|
+
@click="testCard(card.number)"
|
|
470
|
+
class="test-button"
|
|
471
|
+
>
|
|
472
|
+
{{ card.name }}
|
|
473
|
+
</button>
|
|
474
|
+
</div>
|
|
475
|
+
</div>
|
|
476
|
+
</div>
|
|
477
|
+
</template>
|
|
478
|
+
|
|
479
|
+
<style module>
|
|
480
|
+
.detection-demo {
|
|
481
|
+
max-width: 600px;
|
|
482
|
+
padding: 1.5rem;
|
|
483
|
+
border: 1px solid #e0e0e0;
|
|
484
|
+
border-radius: 8px;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.demo-input {
|
|
488
|
+
margin-bottom: 2rem;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.detection-info {
|
|
492
|
+
margin-top: 1rem;
|
|
493
|
+
padding: 1rem;
|
|
494
|
+
background-color: #f8f9fa;
|
|
495
|
+
border-radius: 4px;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.detection-info p {
|
|
499
|
+
margin: 0.25rem 0;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.test-cards h4 {
|
|
503
|
+
margin-bottom: 1rem;
|
|
504
|
+
color: #333;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.card-buttons {
|
|
508
|
+
display: grid;
|
|
509
|
+
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
|
510
|
+
gap: 0.5rem;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.test-button {
|
|
514
|
+
padding: 0.5rem;
|
|
515
|
+
background-color: #6c757d;
|
|
516
|
+
color: white;
|
|
517
|
+
border: none;
|
|
518
|
+
border-radius: 4px;
|
|
519
|
+
cursor: pointer;
|
|
520
|
+
font-size: 0.9rem;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.test-button:hover {
|
|
524
|
+
background-color: #5a6268;
|
|
525
|
+
}
|
|
526
|
+
</style>
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### Validation Feedback
|
|
530
|
+
|
|
531
|
+
```vue
|
|
532
|
+
<script setup lang="ts">
|
|
533
|
+
import { ref, computed } from "vue";
|
|
534
|
+
import { InputCard } from "@umbra-ui/core";
|
|
535
|
+
|
|
536
|
+
const cardNumber = ref("");
|
|
537
|
+
const isValid = ref(false);
|
|
538
|
+
const isComplete = ref(false);
|
|
539
|
+
|
|
540
|
+
const validationMessage = computed(() => {
|
|
541
|
+
if (!isComplete.value) {
|
|
542
|
+
return "Enter your card number";
|
|
543
|
+
}
|
|
544
|
+
if (!isValid.value) {
|
|
545
|
+
return "Please enter a valid card number";
|
|
546
|
+
}
|
|
547
|
+
return "Card number is valid";
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
const validationClass = computed(() => {
|
|
551
|
+
if (!isComplete.value) return "neutral";
|
|
552
|
+
return isValid.value ? "valid" : "invalid";
|
|
553
|
+
});
|
|
554
|
+
</script>
|
|
555
|
+
|
|
556
|
+
<template>
|
|
557
|
+
<div class="validation-example">
|
|
558
|
+
<h3>Validation Feedback</h3>
|
|
559
|
+
|
|
560
|
+
<div class="form-group">
|
|
561
|
+
<label>Card Number</label>
|
|
562
|
+
<InputCard
|
|
563
|
+
v-model:value="cardNumber"
|
|
564
|
+
@update:valid="
|
|
565
|
+
(valid) => {
|
|
566
|
+
isValid = valid;
|
|
567
|
+
isComplete = cardNumber.length > 0;
|
|
568
|
+
}
|
|
569
|
+
"
|
|
570
|
+
placeholder="1234 5678 9012 3456"
|
|
571
|
+
/>
|
|
572
|
+
|
|
573
|
+
<div :class="['validation-message', validationClass]">
|
|
574
|
+
{{ validationMessage }}
|
|
575
|
+
</div>
|
|
576
|
+
</div>
|
|
577
|
+
</div>
|
|
578
|
+
</template>
|
|
579
|
+
|
|
580
|
+
<style module>
|
|
581
|
+
.validation-example {
|
|
582
|
+
max-width: 400px;
|
|
583
|
+
padding: 1.5rem;
|
|
584
|
+
border: 1px solid #e0e0e0;
|
|
585
|
+
border-radius: 8px;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.form-group {
|
|
589
|
+
margin-bottom: 1rem;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
label {
|
|
593
|
+
display: block;
|
|
594
|
+
margin-bottom: 0.5rem;
|
|
595
|
+
font-weight: 500;
|
|
596
|
+
color: #333;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.validation-message {
|
|
600
|
+
margin-top: 0.5rem;
|
|
601
|
+
padding: 0.5rem;
|
|
602
|
+
border-radius: 4px;
|
|
603
|
+
font-size: 0.9rem;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.validation-message.neutral {
|
|
607
|
+
background-color: #e9ecef;
|
|
608
|
+
color: #6c757d;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.validation-message.valid {
|
|
612
|
+
background-color: #d4edda;
|
|
613
|
+
color: #155724;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
.validation-message.invalid {
|
|
617
|
+
background-color: #f8d7da;
|
|
618
|
+
color: #721c24;
|
|
619
|
+
}
|
|
620
|
+
</style>
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
## Notes
|
|
624
|
+
|
|
625
|
+
- The component automatically formats card numbers with appropriate spacing based on the detected card type
|
|
626
|
+
- Card validation uses the Luhn algorithm for accurate validation
|
|
627
|
+
- The component prevents non-numeric input and enforces maximum length limits per card type
|
|
628
|
+
- Card type detection happens in real-time as the user types
|
|
629
|
+
- The component includes smooth animations for card type icons and error states
|
|
630
|
+
- Visual feedback includes card brand icons and validation error indicators
|
|
631
|
+
- The component supports paste operations with automatic formatting
|
|
632
|
+
- All major credit card types are supported with their specific formatting rules
|
|
633
|
+
- The component is fully accessible with proper ARIA attributes and keyboard navigation
|
|
634
|
+
- Error states are automatically triggered when a complete but invalid card number is entered
|
|
635
|
+
- The component includes responsive design considerations for mobile devices
|
|
636
|
+
- Input is restricted to numeric characters only with proper input mode settings
|