Source: contentDocumentTable/contentDocumentTable.js

import { refreshApex } from '@salesforce/apex';
import getDocuments from '@salesforce/apex/ContentDocumentController.getDocuments';
import getLatestVersion from '@salesforce/apex/ContentDocumentController.getLatestVersion';
import { NavigationMixin } from 'lightning/navigation';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { deleteRecord } from 'lightning/uiRecordApi';
import { api, LightningElement, track, wire } from 'lwc';

const ICONS = {
  csv: 'doctype:csv',
  default: 'doctype:attachment',
  docx: 'doctype:word',
  jpeg: 'doctype:image',
  jpg: 'doctype:image',
  pdf: 'doctype:pdf',
  png: 'doctype:image',
  pptx: 'doctype:ppt',
  rtf: 'doctype:rtf',
  txt: 'doctype:txt',
  xlsx: 'doctype:excel',
  xml: 'doctype:xml',
  zip: 'doctype:zip'
};

const COLUMNS = [
  {
    label: '',
    fieldName: 'icon',
    type: 'contentDocumentIcon',
    hideDefaultActions: true,
    fixedWidth: 40
  },
  {
    label: 'Title',
    fieldName: 'preview',
    type: 'contentDocumentPreview',
    hideDefaultActions: true
  },
  {
    label: 'Size',
    fieldName: 'ContentSize',
    hideDefaultActions: true,
    initialWidth: 80,
    cellAttributes: { alignment: 'right' }
  },
  {
    label: 'Created',
    fieldName: 'CreatedDate',
    type: 'date',
    hideDefaultActions: true,
    initialWidth: 100
  }
];

/**
 * A generic table to show shared documents from a Salesforce Files library.
 * @alias ContentDocumentTable
 * @extends LightningElement
 * @hideconstructor
 *
 * @example
 * <c-content-document-table library="Documents" record-id={recordid}></c-content-document-table>
 */
export default class ContentDocumentTable extends NavigationMixin(LightningElement) {
  /**
   * If show card option is active,, the card icon is displayed in the header before the card title.
   * It should contain the SLDS name of the icon.
   * Specify the name in the format 'standard:case' where 'standard' is the category and 'case' the icon to be displayed.
   * @type {string}
   * @default ''
   * @example 'standard:case'
   */
  @api cardIcon = 'standard:file';

  /**
   * If show card option is active, The card title can include text and is displayed in the header above the table.
   * @type {string}
   * @default 'Document Table'
   */
  @api cardTitle = 'Document Table';

  /**
   * Folder name within Files library.
   * @type {string}
   */
  @api folder = null;

  /**
   * Files library name.
   * @type {string}
   * @default 'Documents'
   */
  @api library = 'Documents';

  /**
   * If the component is used on a lightning record page, the page sets the property to the id of the current record.
   * @type {string}
   */
  @api recordId = null;

  /**
   * If present, the table is wrapped in a lightning card to fit better into the overall page layout.
   * @type {boolean}
   * @default false
   */
  @api showCard = false;

  /**
   * If present, the last column contains a delete file action.
   * @type {boolean}
   * @default false
   */
  @api showDeleteAction = false;

  /**
   * If present, the last column contains a download file action.
   * @type {boolean}
   * @default false
   */
  @api showDownloadAction = false;

  /**
   * If present, the last column contains a view file action.
   * @type {boolean}
   * @default false
   */
  @api showViewAction = false;

  @track columns = COLUMNS;
  @track documents = [];
  @track wiredRecords = [];

  isLoading = true;

  @wire(getDocuments, {
    library: '$library',
    folder: '$folder',
    recordId: '$recordId'
  })
  wiredGetRecords(result) {
    this.wiredRecords = result;

    if (result.data) {
      const documents = JSON.parse(result.data);

      if (Array.isArray(documents)) {
        documents.forEach((element) => {
          element.icon = ICONS[element.FileExtension] ? ICONS[element.FileExtension] : ICONS.default;
          element.preview = {
            id: element.Id,
            name: element.Title
          };
          if (element.ContentSize) element.ContentSize = this.formatSize(element.ContentSize);
        });
        this.documents = JSON.parse(JSON.stringify(documents));
      }
      this.isLoading = false;
    }
  }

  connectedCallback() {
    this.columns = COLUMNS.slice();
    this.addRowActions();
  }

  addRowActions() {
    const actions = [];
    if (this.showDownloadAction) actions.push({ label: 'Download', name: 'download' });
    if (this.showViewAction) actions.push({ label: 'View', name: 'view' });
    if (this.showDeleteAction) actions.push({ label: 'Delete', name: 'delete' });
    if (actions.length) {
      this.columns.push({ type: 'action', typeAttributes: { rowActions: actions, menuAlignment: 'right' } });
    }
  }

  handleRowAction(event) {
    const actionName = event.detail.action.name;
    const row = event.detail.row;
    switch (actionName) {
      case 'download':
        this.download(row);
        break;
      case 'view':
        this.view(row);
        break;
      case 'delete':
        this.delete(row);
        break;
      default:
        break;
    }
  }

  download(row) {
    getLatestVersion({ recordId: row.Id })
      .then((version) => {
        window.open(`/sfc/servlet.shepherd/version/download/${version}`);
      })
      .catch((error) => {
        this.showToast('Error downloading file', error?.body?.message, 'error');
      });
  }

  view(row) {
    this[NavigationMixin.Navigate]({
      type: 'standard__recordPage',
      attributes: {
        recordId: row.Id,
        objectApiName: 'ContentDocument',
        actionName: 'view'
      }
    });
  }

  delete(row) {
    this.isLoading = true;
    deleteRecord(row.Id)
      .then(() => {
        this.showToast('Success', 'File deleted', 'success');
        return refreshApex(this.wiredRecords).then(() => {
          this.isLoading = false;
        });
      })
      .catch((error) => {
        this.showToast('Error deleting file', error?.body?.message, 'error');
      });
  }

  showToast(title, message, variant) {
    this.dispatchEvent(new ShowToastEvent({ title: title, message: message, variant: variant }));
  }

  formatSize(size) {
    return size && size / 1000000 >= 1 ? `${(size / 1000000).toFixed(1)} MB` : `${parseInt(size / 1000, 10)} KB`;
  }
}