Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Docs] Examples #297

Closed
benjamincanac opened this issue Jun 16, 2023 — with Volta.net · 14 comments
Closed

[Docs] Examples #297

benjamincanac opened this issue Jun 16, 2023 — with Volta.net · 14 comments
Labels
documentation Improvements or additions to documentation

Comments

Copy link
Member

benjamincanac commented Jun 16, 2023

Feel free to comment here if you have some examples you'd like to see on the documentation: https://ui.nuxtlabs.com/getting-started/examples

@benjamincanac benjamincanac added the documentation Improvements or additions to documentation label Jun 16, 2023 — with Volta.net
@Haythamasalama
Copy link
Contributor

Haythamasalama commented Jun 17, 2023

✨ I'm preparing a PR for an advanced example table that is used in something similar to my own component in a real-life project. I'm currently working on it and have rebuilt it using Nuxt Lab UI.

The table features:

  • Auto-generated filter forms (text, radio, select, datepicker).
  • Columns management: sorting, show/hide, and action to save it (either to your backend or local storage).
  • Pagination and metadata for the number of rows.
  • Menu for selecting the number of rows per page.
  • Quick action buttons (e.g., Add).
  • Clear filter and reset action with logic to reset all operations, such as pagination.
  • Loading and empty states.
  • API and server-side integration.

What do you think about providing this as an example? Everyone should be able to wrap it or turn it into single components.

Currently, I've coded 452 lines, and I'm not finished yet!

I have a question regarding mocking or using the API service for this example to simulate these operations. What is your opinion?"

table.mp4

Copy link
Member Author

It would be indeed really nice to have a full data-table example like on https://ui.shadcn.com/examples/tasks. I'd keep the example as simple as possible without any API mocking by hard-coding the data though.

Feel free to submit a PR :)

@Haythamasalama
Copy link
Contributor

To be honest, the table example with API is missing in the docs, and this is always used with the filter form API and reset pagination, filitering, etc., which are needed in real life. I will make it as simple as possible.

@ddahan
Copy link

ddahan commented Jul 29, 2023

I would love to have an example for form validation using Nuxt Labs UI form components and a library like VeeValidate.

Copy link
Member Author

#439 is coming soon for this!

@ddahan
Copy link

ddahan commented Aug 17, 2023

I was wondering if it makes sense to add an example with SortableJS using VueUse?

I mean, the code itself would have no interest because it's very easy to implement. However, the presence of such an example in the doc would be a simple way to remind people it exists, and hence they don't need to use another UI library that would implement it from scratch (kind of the same logic the the date picker example, but in simpler).

@ddahan
Copy link

ddahan commented Aug 18, 2023

Another interesting example could be a file upload mechanism using https://vueuse.org/core/useFileDialog/#usefiledialog

@rahul37865
Copy link

I would love to have an example for form validation using Nuxt Labs UI form components and a library like VeeValidate.

Have you implemented VeeValidate + Yup with NuxtLabs UI ? It would be very helpful if you could write an example.

@oritwoen
Copy link
Contributor

I would love to have an example for form validation using Nuxt Labs UI form components and a library like VeeValidate.

Have you implemented VeeValidate + Yup with NuxtLabs UI ? It would be very helpful if you could write an example.

It's already available: https://ui.nuxtlabs.com/forms/form

@geekyshow1
Copy link

Recently VeeValidate official have created an example for vee-validate + yup + nuxtlabs UI Click Here
However there is minor issue with this example which i have reported to VeeValidate official.
I would request Nuxtlabs UI kindly add above mentioned example on your website https://ui.nuxtlabs.com/forms/form#other-libraries

@geekyshow1
Copy link

Hi, As There are many Form Component which are still on development or at present not available I have created a basic Form which i am sharing feel free to use it in your project or NuxtLabs UI Team can add it on their Example Doc. suggestions and enhancements are welcome.

<script setup>
import { object, string } from 'yup';
const form = ref()
const selectedImage = ref('');
const imageUploadError = ref('');
const documentUploadError = ref(null);
const previewUrl = ref('');
const allStates = ['Jharkhand', 'Bihar', 'West Bengal']
const allLocations = ['Delhi', 'Banglore', 'Kolkata', 'Ranchi', 'Bokaro']
const allGenders = [{
  name: 'male',
  value: 'male',
  label: 'Male'
}, {
  name: 'female',
  value: 'female',
  label: 'Female'
}]
const formData = ref({
  name: '',
  email: '',
  dob: '',
  state: '',
  gender: 'male',
  location: [],
  pimage: undefined,
  rdoc: undefined,
});

// Using Yup to validate the form. You can add other fields too except pimage and rdoc
// We will manually validate pimage and rdoc
const schema = object({
  name: string().required('Name is required'),
  email: string().email().required('Email is required'),
})

const handleImageUpload = (event) => {
  const selectedFile = event.target.files[0];

  // Manually Validating pimage field on change
  // Check if the selected file is an image and meets the size criteria
  if (selectedFile) {
    if (selectedFile.type.startsWith('image/') && selectedFile.size <= 300 * 1024) {
      // Valid image: update selectedImage, formData.pimage, and previewUrl
      selectedImage.value = selectedFile;
      formData.pimage = selectedFile;

      // Clear any previous error message
      imageUploadError.value = null;

      // To Preview Selected Image
      const reader = new FileReader();
      reader.onload = (event) => {
        previewUrl.value = event.target.result;
      };
      reader.readAsDataURL(selectedFile);
    } else {
      // Invalid image: clear selectedImage and formData.pimage
      selectedImage.value = null;
      formData.pimage = null;

      // Clear previewUrl
      previewUrl.value = '';

      // Set the error message
      imageUploadError.value = 'Invalid image. Please select a valid image (JPEG, JPG, or PNG) with a size <= 300KB.';
    }
  }
};
const handleDocumentUpload = (event) => {
  const selectedFile = event.target.files[0];

  // Manually Validating rdoc field on change
  // Check if the selected file is a valid document and meets the size criteria
  if (selectedFile) {
    const allowedFormats = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
    const maxSize = 2 * 1024 * 1024; // 2MB

    if (allowedFormats.includes(selectedFile.type) && selectedFile.size <= maxSize) {
      // Valid document: update formData.rdoc
      formData.rdoc = selectedFile;

      // Clear any previous error message
      documentUploadError.value = null;
    } else {
      // Invalid document: clear formData.rdoc
      formData.rdoc = null;

      // Set the error message
      documentUploadError.value = 'Invalid document. Please select a valid document (PDF, DOC, or DOCX) with a size <= 2MB.';
    }
  }
};

const submitForm = async () => {
  await form.value.validate()

  // checking image and file validation error on form submit
  if (imageUploadError.value || documentUploadError.value) {
      console.error('Form validation failed');
      return; // Exit the function if there are validation errors
    }
   // Change apiURL according to your project requirement 
  const apiUrl = 'http://127.0.0.1:8000/api/resume/';
 
  // Create a FormData object and append form data
  const data = new FormData();
  data.append('name', formData.value.name);
  data.append('email', formData.value.email);
  data.append('dob', formData.value.dob);
  data.append('state', formData.value.state);
  data.append('gender', formData.value.gender);
  data.append('location', formData.value.location);
  data.append('pimage', formData.pimage);
  data.append('rdoc', formData.rdoc);
  try {
    const {data:user, error} = await useFetch(apiUrl, {
      method: 'POST',
      body: data
     // Do not include Headers. Let it handle by useFetch. Including headers e.g. multipart may cause error.
    });
    if (user.value) {
      // Handle a successful API response here
      console.log('Form data submitted successfully');
    }
    if (error.value) {
      // Handle a error API response here
      console.log('There is something wrong', error.value);
    }
  } catch (error) {
    console.error('Error:', error);
  }
};
</script>

<template>
  <UContainer>
    <UForm ref="form" :schema="schema" :state="formData" @submit.prevent="submitForm" class="max-w-2xl shadow-md p-10 shadow-orange-300">
      <UFormGroup label="Name" name="name" class="mb-4">
        <UInput v-model="formData.name" class="px-3 py-2 border rounded" />
      </UFormGroup>
      <UFormGroup label="Email" name="email" class="mb-4">
        <UInput v-model="formData.email" class="px-3 py-2 border rounded" />
      </UFormGroup>
      <UFormGroup label="Date of Birth" name="dob" class="mb-4">
        <input type="date" id="dob" v-model="formData.dob" class="w-full px-3 py-2 dark:bg-gray-800 dark:border-0 border-2 rounded"/>
      </UFormGroup>
      <UFormGroup label="State" name="state" class="mb-4">
        <USelect v-model="formData.state" :options="allStates" class="px-3 py-2 border rounded" />
      </UFormGroup>
      <UFormGroup label="Gender" name="gender" class="mb-4">
        <div class="flex space-x-4">
          <URadio v-for="gdr of allGenders" :key="gdr.name" v-model="formData.gender" v-bind="gdr" class="mr-2" />
        </div>
      </UFormGroup>
      <UFormGroup label="Job Location" name="location" class="mb-4">
        <USelectMenu id="location" v-model="formData.location" :options="allLocations" multiple placeholder="Select location" />
      </UFormGroup>
      <UFormGroup label="Profile Image (JPEG/JPG/PNG - less than 300KB)" name="pimage" class="mb-4">
        <div class="flex items-center space-x-6">
          <div class="shrink-0" v-if="selectedImage">
            <img class="h-16 w-16 object-cover rounded-full" :src="previewUrl" alt="Current profile photo" />
          </div>
          <label class="block">
            <span class="sr-only">Choose profile photo</span>
            <input type="file" id="pimage" name="pimage" class="text-sm text-slate-500 dark:text-gray-100
              file:mr-4 file:py-1 file:px-3
              file:rounded-full file:border-0
              file:text-xs 
              file:bg-violet-50 file:text-violet-700
              hover:file:bg-violet-100"
              accept="image/jpeg, image/png, image/jpg" 
              @change="handleImageUpload"
            />
          </label>
        </div>
        <!-- Display an error message if imageUploadError is not null -->
        <p v-if="imageUploadError" class="text-red-500 text-sm mt-2">{{ imageUploadError }}</p>
      </UFormGroup>
      <UFormGroup label="Resume (Doc/Docx/PDF - less than 2MB)" name="rdoc" class="mb-4">
        <span class="sr-only">Choose Resume File</span>
        <input type="file" id="rdoc" name="rdoc" class="text-sm text-slate-500  dark:text-gray-100
          file:mr-4 file:py-1 file:px-3
          file:rounded-full file:border-0
          file:text-xs 
          file:bg-violet-50 file:text-violet-700
          hover:file:bg-violet-100"
          accept=".pdf,.doc,.docx" 
          @change="handleDocumentUpload"
        />
        <!-- Display an error message if documentUploadError is not null -->
        <p v-if="documentUploadError" class="text-red-500 text-sm mt-2">{{ documentUploadError }}</p>
      </UFormGroup>
      <UButton type="submit">Submit</UButton>
    </UForm>
  </UContainer>
</template>

<style scoped>
</style>

Note - I have used yup so you need to install yup using npm i yup

@madebyfabian
Copy link
Contributor

madebyfabian commented Sep 15, 2023

Wanted to share this implementation of the pagination, to make the prev/next buttons to be <a> elements, to improve SEO.

If you have an idea how I can implement it so that the page (1, 2, 3, ...) buttons also render as <a> elements, let me know!

<template>
  <div class="container">
    <div class="flex justify-center mt-8 md:mt-12">
      <UPagination
        v-model="page"
        :page-count="pageCount"
        :total="state.storiesTotal"
        :prevButton="prevPageProps"
        :nextButton="nextPageProps" />
    </div>
  </div>
</template>
<script setup lang="ts">
  import type { Button } from '@nuxt/ui/dist/runtime/types'

  // Amount of articles per page
  const pageCount = ref(3)
  
  // Current page count
  // Custom composable that sets a query parameter.
  const { searchQuery } = useRouteSearchQuery({ name: 'page', defaultValue: '1' })
  const page = computed<number>({
  	get: () => Number(searchQuery.value),
  	set: value => {
  		searchQuery.value = String(value)
  	},
  })
  
  // Pagination UI props, used to render the prev/next buttons as `<a>` (due to the `to` prop)
  const nextPageProps = computed<Partial<Button> | undefined>(() => {
    const canGoNext = page.value * pageCount.value < state.value.storiesTotal
    return canGoNext ? { to: { query: { page: page.value + 1 } } } : undefined
  })
  const prevPageProps = computed<Partial<Button> | undefined>(() => {
    const canGoPrev = page.value > 1
    return canGoPrev ? { to: { query: { page: page.value - 1 } } } : undefined
  })
  
  // Data fetch
  // Custom data fetching composable, not important for the example
  const { state, fetchStories } = await useStoryList({
  	key: 'ratgeber-alle-artikel',
  	init: {
  		...defaultOptions.value,
  		page: page.value,
  	},
  })
  watch(page, () => {
  	fetchStories({
  		...defaultOptions.value,
  		page: page.value,
  	})
  })
</script>

@clopezpro
Copy link
Contributor

✨ I'm preparing a PR for an advanced example table that is used in something similar to my own component in a real-life project. I'm currently working on it and have rebuilt it using Nuxt Lab UI.

The table features:

  • Auto-generated filter forms (text, radio, select, datepicker).
  • Columns management: sorting, show/hide, and action to save it (either to your backend or local storage).
  • Pagination and metadata for the number of rows.
  • Menu for selecting the number of rows per page.
  • Quick action buttons (e.g., Add).
  • Clear filter and reset action with logic to reset all operations, such as pagination.
  • Loading and empty states.
  • API and server-side integration.

What do you think about providing this as an example? Everyone should be able to wrap it or turn it into single components.

Currently, I've coded 452 lines, and I'm not finished yet!

I have a question regarding mocking or using the API service for this example to simulate these operations. What is your opinion?"

table.mp4

I would love to see this example

@aastrum
Copy link

aastrum commented Nov 28, 2023

Plese make range from - to, input autocomplete with hints. And it will be also good if you make float label in control fields.

@benjamincanac benjamincanac closed this as not planned Won't fix, can't repro, duplicate, stale Jan 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

9 participants