Skip to content
This repository was archived by the owner on Mar 27, 2025. It is now read-only.

Commit 85396d3

Browse files
committed
feat(BFormFile): add in BFormFile
fix: no-dupe issues
1 parent 02a9118 commit 85396d3

File tree

26 files changed

+564
-102
lines changed

26 files changed

+564
-102
lines changed

apps/docs/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "0.0.1",
44
"private": true,
55
"scripts": {
6-
"dev": "vitepress dev",
6+
"dev": "vitepress dev --port 8000",
77
"build": "cross-env NODE_OPTIONS=--max-old-space-size=4096 vitepress build",
88
"preview": "vitepress preview",
99
"lint": "eslint --ext .js,.ts,.vue --ignore-path ../../.gitignore --fix src",

apps/docs/src/components/TableOfContentsNav.vue

+2-3
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@
2020
</BListGroup>
2121
<BListGroup>
2222
<strong class="bd-links-heading">
23-
<BLink :to="withBase('/docs/components')">
24-
<IntersectIcon aria-hidden /> Components
25-
</BLink>
23+
<BLink :to="withBase('/docs/components')"> <IntersectIcon aria-hidden /> Components </BLink>
2624
</strong>
2725
<BListGroupItem v-for="component in componentsComputedList" :key="component.name">
2826
<BLink :to="component.route">{{ component.name }}</BLink>
@@ -90,6 +88,7 @@ const componentsList: {name: string}[] = [
9088
{name: 'Dropdown'},
9189
{name: 'Form'},
9290
{name: 'Form Checkbox'},
91+
{name: 'Form File'},
9392
{name: 'Form Group'},
9493
{name: 'Form Input'},
9594
{name: 'Form Radio'},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import type {ComponentReference} from './ComponentReference'
2+
3+
export default {
4+
load: (): ComponentReference[] => [
5+
{
6+
component: 'BFormFile',
7+
props: [
8+
{prop: 'accept', type: 'string | string[]', description: '', default: "''"},
9+
{prop: 'autofocus', type: 'Booleanish', description: '', default: 'false'},
10+
{
11+
prop: 'capture',
12+
type: "Booleanish | 'user' | 'environment'",
13+
description: '',
14+
default: 'false',
15+
},
16+
{prop: 'directory', type: 'Booleanish', description: '', default: 'false'},
17+
{prop: 'disabled', type: 'Booleanish', description: '', default: 'false'},
18+
{prop: 'form', type: 'string', description: '', default: 'undefined'},
19+
{prop: 'id', type: 'string', description: '', default: 'undefined'},
20+
{prop: 'multiple', type: 'Booleanish', description: '', default: 'false'},
21+
{prop: 'name', type: 'string', description: '', default: 'undefined'},
22+
{prop: 'noDrop', type: 'Booleanish', description: '', default: 'false'},
23+
{prop: 'noTraverse', type: 'Booleanish', description: '', default: 'false'},
24+
{prop: 'required', type: 'Booleanish', description: '', default: 'false'},
25+
{prop: 'size', type: 'Size', description: '', default: 'undefined'},
26+
{prop: 'state', type: 'Booleanish | null', description: '', default: 'null'},
27+
{prop: 'modelValue', type: 'File[] | File | null', description: '', default: 'null'},
28+
{prop: 'label', type: 'string', description: '', default: "''"},
29+
{prop: 'wrapperClasses', type: 'ClassValue', description: '', default: 'undefined'},
30+
{prop: 'labelClasses', type: 'ClassValue', description: '', default: 'undefined'},
31+
],
32+
emits: [
33+
{
34+
event: 'update:modelValue',
35+
description: '',
36+
args: [
37+
{
38+
arg: 'value',
39+
type: 'File | File[] | null',
40+
description: '',
41+
},
42+
],
43+
},
44+
{
45+
event: 'change',
46+
description: '',
47+
args: [
48+
{
49+
arg: 'value',
50+
type: 'Event',
51+
description: 'The browsers default change event',
52+
},
53+
],
54+
},
55+
],
56+
slots: [
57+
{
58+
name: 'label',
59+
description: '',
60+
scope: [],
61+
},
62+
],
63+
},
64+
],
65+
}
+236
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
# Form File
2+
3+
<ClientOnly>
4+
<Teleport to=".bd-toc">
5+
6+
[[toc]]
7+
8+
</Teleport>
9+
</ClientOnly>
10+
11+
<div class="lead mb-5">
12+
13+
File input control that supports single and multiple file modes
14+
15+
</div>
16+
17+
<BAlert :model-value="true" variant="danger">
18+
The current variation is subject to change pre v1.0. The implementation may change to become closer to the Bootstrap-vue implementation based on feedback <BLink target="_blank" href="https://github.com/bootstrap-vue-next/bootstrap-vue-next/discussions/1213" rel="noopener">vote here</BLink>
19+
</BAlert>
20+
21+
## Single File Mode
22+
23+
The default behavior is single file mode. While using single file mode the `modelValue` will be a single `File` object
24+
25+
<HighlightCard>
26+
<BFormFile v-model="first" label="Hello!" />
27+
<div class="mt-3">
28+
File: <strong>{{ first }}</strong>
29+
</div>
30+
<template #html>
31+
32+
```vue
33+
<template>
34+
<BFormFile v-model="file" label="Hello!" />
35+
<div class="mt-3">
36+
Files: <strong>{{ file }}</strong>
37+
</div>
38+
</template>
39+
40+
<script setup lang="ts">
41+
const file = ref<null | File>(null)
42+
</script>
43+
```
44+
45+
</template>
46+
</HighlightCard>
47+
48+
## Multiple File Mode
49+
50+
To toggle multiple file mode, simply set the `multiple` prop to `true`. While in multiple file mode, the `modelValue` will be a `File[]`, even if only one file is selected
51+
52+
<HighlightCard>
53+
<BFormFile v-model="second" multiple />
54+
<div class="mt-3">
55+
Files: <strong>{{ second }}</strong>
56+
</div>
57+
<template #html>
58+
59+
```vue
60+
<template>
61+
<BFormFile v-model="files" multiple />
62+
<div class="mt-3">
63+
Files: <strong>{{ files }}</strong>
64+
</div>
65+
</template>
66+
67+
<script setup lang="ts">
68+
const files = ref<null | File[]>(null)
69+
</script>
70+
```
71+
72+
</template>
73+
</HighlightCard>
74+
75+
## Limiting to certain file types
76+
77+
You can limit the file types by setting the `accept` prop. The `accept` attribute is a csv list of acceptable types. This can be a `string` or `string[]`. If a `string[]` is inputted, it simply gets joined as a csv list
78+
79+
<HighlightCard>
80+
<BFormFile v-model="third" accept="image/*" />
81+
<div class="mt-3">
82+
File: <strong>{{ third }}</strong>
83+
</div>
84+
<template #html>
85+
86+
```vue
87+
<template>
88+
<BFormFile v-model="file" accept="image/*" />
89+
<div class="mt-3">
90+
Files: <strong>{{ file }}</strong>
91+
</div>
92+
</template>
93+
94+
<script setup lang="ts">
95+
const file = ref<null | File>(null)
96+
</script>
97+
```
98+
99+
</template>
100+
</HighlightCard>
101+
102+
## Drag and Drop Support
103+
104+
Drag and drop support uses the browsers default behavior. You can explicitly disable drag and drop by using the `noDrop` prop
105+
106+
<HighlightCard>
107+
<BFormFile v-model="fourth" no-drop />
108+
<div class="mt-3">
109+
File: <strong>{{ fourth }}</strong>
110+
</div>
111+
<template #html>
112+
113+
```vue
114+
<template>
115+
<BFormFile v-model="file" no-drop />
116+
<div class="mt-3">
117+
Files: <strong>{{ file }}</strong>
118+
</div>
119+
</template>
120+
121+
<script setup lang="ts">
122+
const file = ref<null | File>(null)
123+
</script>
124+
```
125+
126+
</template>
127+
</HighlightCard>
128+
129+
## Sizing
130+
131+
You can modify the size of the form control by using the `size` prop
132+
133+
<HighlightCard>
134+
<BFormFile class="mt-3" size="sm" />
135+
<BFormFile class="mt-3" />
136+
<BFormFile class="mt-3" size="lg" />
137+
138+
<template #html>
139+
140+
```vue-html
141+
<BFormFile class="mt-3" size="sm" />
142+
<BFormFile class="mt-3" />
143+
<BFormFile class="mt-3" size="lg" />
144+
```
145+
146+
</template>
147+
</HighlightCard>
148+
149+
## Label
150+
151+
You can add a label above the input by using the `label` prop or the `label` slot
152+
153+
<HighlightCard>
154+
<BFormFile label="I'm first!" />
155+
<BFormFile>
156+
<template #label>
157+
I'm second!
158+
</template>
159+
</BFormFile>
160+
161+
<template #html>
162+
163+
```vue-html
164+
<BFormFile class="mt-3" label="I'm first!" />
165+
<BFormFile class="mt-3">
166+
<template #label>
167+
I'm second!
168+
</template>
169+
</BFormFile>
170+
```
171+
172+
</template>
173+
</HighlightCard>
174+
175+
## Directory Mode
176+
177+
By adding the `directory` prop, a user can select directories instead of files
178+
179+
<BAlert variant="danger" :model-value="true">
180+
Directory mode is a non-standard attribute in the HTML spec. All major browsers have chosen too support it, but it may not function correctly for browsers that have chosen not to implement it. Use with caution
181+
</BAlert>
182+
183+
### Example to be Written
184+
185+
## Autofocus
186+
187+
If you set the `autofocus` prop to true, the input will be focused when the component is inserted
188+
189+
<HighlightCard>
190+
<BFormFile class="mt-3" autofocus />
191+
192+
<template #html>
193+
194+
```vue-html
195+
<BFormFile class="mt-3" autofocus />
196+
```
197+
198+
</template>
199+
</HighlightCard>
200+
201+
## Contextual State
202+
203+
You can use the `state` prop to provide visual feedback on the state of the input
204+
205+
<HighlightCard>
206+
<BFormFile class="mt-3" :state="false" />
207+
<BFormFile class="mt-3" :state="true" />
208+
209+
<template #html>
210+
211+
```vue-html
212+
<BFormFile class="mt-3" :state="false" />
213+
<BFormFile class="mt-3" :state="true" />
214+
```
215+
216+
</template>
217+
</HighlightCard>
218+
219+
## Modifying the file selection
220+
221+
With inputs that are of type `file`, the value is strictly `uni-directional`. Meaning that you cannot change the value of the input via JavaScript. You can change the value of the `v-model`, and this will work for an "outside view", however, the actual `input` element will not have its [FileList](https://developer.mozilla.org/en-US/docs/Web/API/FileList) changed. This is for security reasons as a malicious script could attempt to read and steal documents
222+
223+
<ComponentReference :data="data" />
224+
225+
<script setup lang="ts">
226+
import {data} from '../../data/components/formFile.data'
227+
import ComponentReference from '../../components/ComponentReference.vue'
228+
import HighlightCard from '../../components/HighlightCard.vue'
229+
import {BFormFile, BAlert, BLink} from 'bootstrap-vue-next'
230+
import {ref} from 'vue'
231+
232+
const first = ref(null)
233+
const second = ref(null)
234+
const third = ref(null)
235+
const fourth = ref(null)
236+
</script>

packages/bootstrap-vue-next/src/BootstrapVue.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import * as Directives from './directives'
88

99
declare module '@vue/runtime-core' {
1010
export interface GlobalComponents {
11+
BFormFile: typeof Components.BFormFile
1112
BAccordion: typeof Components.BAccordion
1213
BAccordionItem: typeof Components.BAccordionItem
1314
BAlert: typeof Components.BAlert

packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue

+2-7
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,7 @@
1818
{{ closeContent }}
1919
</slot>
2020
</BButton>
21-
<BCloseButton
22-
v-else
23-
:aria-label="dismissLabel"
24-
v-bind="closeAttrs"
25-
@click="closeClicked"
26-
/>
21+
<BCloseButton v-else :aria-label="dismissLabel" v-bind="closeAttrs" @click="closeClicked" />
2722
</template>
2823
</div>
2924
</BTransition>
@@ -91,7 +86,7 @@ const immediateBoolean = useBooleanish(() => props.immediate)
9186
const showOnPauseBoolean = useBooleanish(() => props.showOnPause)
9287
const noHoverPauseBoolean = useBooleanish(() => props.noHoverPause)
9388
94-
const hasCloseSlot = computed<boolean>(() => !isEmptySlot(slots.close))
89+
const hasCloseSlot = computed(() => !isEmptySlot(slots.close))
9590
9691
const countdownLength = computed(() =>
9792
typeof modelValue.value === 'boolean' ? 0 : modelValue.value

packages/bootstrap-vue-next/src/components/BAvatar/BAvatar.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ const buttonBoolean = useBooleanish(() => props.button)
9898
const disabledBoolean = useBooleanish(() => props.disabled)
9999
const squareBoolean = useBooleanish(() => props.square)
100100
101-
const hasDefaultSlot = computed<boolean>(() => !isEmptySlot(slots.default))
102-
const hasBadgeSlot = computed<boolean>(() => !isEmptySlot(slots.badge))
101+
const hasDefaultSlot = computed(() => !isEmptySlot(slots.default))
102+
const hasBadgeSlot = computed(() => !isEmptySlot(slots.badge))
103103
104104
const showBadge = computed<boolean>(() => !!props.badge || props.badge === '' || hasBadgeSlot.value)
105105

packages/bootstrap-vue-next/src/components/BCard/BCard.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ const imgEndBoolean = useBooleanish(() => props.imgEnd)
154154
const imgStartBoolean = useBooleanish(() => props.imgStart)
155155
const noBodyBoolean = useBooleanish(() => props.noBody)
156156
157-
const hasHeaderSlot = computed<boolean>(() => !isEmptySlot(slots.header))
158-
const hasFooterSlot = computed<boolean>(() => !isEmptySlot(slots.footer))
157+
const hasHeaderSlot = computed(() => !isEmptySlot(slots.header))
158+
const hasFooterSlot = computed(() => !isEmptySlot(slots.footer))
159159
160160
const computedClasses = computed(() => ({
161161
[`text-${props.align}`]: props.align !== undefined,

packages/bootstrap-vue-next/src/components/BCard/BCardBody.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ const slots = useSlots()
7070
7171
const overlayBoolean = useBooleanish(() => props.overlay)
7272
73-
const hasTitleSlot = computed<boolean>(() => !isEmptySlot(slots.title))
74-
const hasSubtitleSlot = computed<boolean>(() => !isEmptySlot(slots.subtitle))
73+
const hasTitleSlot = computed(() => !isEmptySlot(slots.title))
74+
const hasSubtitleSlot = computed(() => !isEmptySlot(slots.subtitle))
7575
7676
const computedClasses = computed(() => ({
7777
'card-img-overlay': overlayBoolean.value,

0 commit comments

Comments
 (0)