{"version":3,"sources":["webpack:///./app/react/packages/folders/constants/index.js","webpack:///./app/react/packages/folders/context/actionTypes.js","webpack:///./app/react/packages/folders/context/actions.js","webpack:///./app/react/packages/folders/context/helpers/adjustFolder.js","webpack:///./app/react/packages/folders/context/helpers/withoutFolderId.js","webpack:///./app/react/packages/folders/context/helpers/withoutFolderItem.js","webpack:///./app/react/packages/folders/context/helpers/publishedFolders.js","webpack:///./app/react/packages/folders/context/helpers/sharedFolders.js","webpack:///./app/react/packages/folders/context/reducer.js","webpack:///./app/react/packages/folders/context/helpers/addNewFolder.js","webpack:///./app/react/packages/folders/context/helpers/replaceChangedFolders.js","webpack:///./app/react/packages/folders/services/endpoints.js","webpack:///./app/react/packages/folders/services/createFolder.js","webpack:///./app/react/packages/folders/services/index.js","webpack:///./app/react/packages/folders/services/copyFolder.js","webpack:///./app/react/packages/folders/services/deleteFolder.js","webpack:///./app/react/packages/folders/services/deleteItem.js","webpack:///./app/react/packages/folders/services/getFolderContent.js","webpack:///./app/react/packages/folders/services/getFolders.js","webpack:///./app/react/packages/folders/services/getItemFolders.js","webpack:///./app/react/packages/folders/services/publishFolder.js","webpack:///./app/react/packages/folders/services/putItem.js","webpack:///./app/react/packages/folders/services/sendFolderNotification.js","webpack:///./app/react/packages/folders/services/shareFolder.js","webpack:///./app/react/packages/folders/services/unpublishFolder.js","webpack:///./app/react/packages/folders/services/unshareFolder.js","webpack:///./app/react/packages/folders/services/updateFolder.js","webpack:///./app/react/packages/folders/services/updateFolderPosition.js","webpack:///./app/react/packages/folders/services/updateItemPosition.js","webpack:///./app/react/packages/folders/context/context.js","webpack:///./app/react/packages/folders/components/EmptyState.js","webpack:///./app/react/packages/folders/utils/sort.js","webpack:///./app/react/packages/folders/components/MoveItemModal.js"],"names":["sortTypes","ascending","descending","manual","author","date","itemTypes","course","flashcardCategory","flashcardSet","folder","lesson","post","video","addPublishedFolder","type","actionTypes","payload","adjustFolderShares","createFolder","deleteFolder","folderId","deleteItem","itemId","removeFolderShares","removePublishedFolder","setCurrentFolderId","setFolders","folders","setNewPublished","bool","setNewShared","sortAscending","sortDescending","sortManual","sortSharedAscending","sortSharedAuthor","sortSharedDate","sortSharedDescending","updateChangedFolders","changedFolders","updateFolder","id","name","description","index","Error","withoutFolderId","withoutFolderItem","folderIndex","folderItems","itemIndex","itemToRemove","withFolderPublished","eslPublishedAt","myFolders","withFolderUnpublished","publishedFolders","existingFolderIndex","sharedSchools","sharedUsers","foldersReducer","state","action","currentFolderId","repositionedFolders","map","f","position","adjustFolder","mergedFolders","changedFolder","indexFromMerged","hasNewPublished","hasNewShared","sort","sharedSort","foldersEndpoint","folderItemsEndpoint","copyFolder","xhr","url","method","item","data","folder_items_attributes","fileable_id","fileable","fileable_type","getFolderContent","getFolders","getItemFolders","itemType","publishFolder","publishedAt","esl_published_at","putItem","fileableId","fileableType","inFolders","folder_item","sendFolderNotification","shareFolder","schools","users","unpublishFolder","unshareFolder","updateFolderPosition","updateItemPosition","FoldersState","createContext","FoldersDispatch","useFoldersContext","useContext","dispatch","undefined","FoldersProvider","children","getInitialState","initialState","sharedFolders","school","isPublisher","useReducer","foldersState","setFoldersState","Provider","value","ModalFoldersProvider","fallback","initialFolders","userLoggedIn","useUserAccess","useEffect","services","fetchedFolders","actions","fetchFolders","displayName","propTypes","PropTypes","node","isRequired","EmptyState","message","py","px","textAlign","bg","src","FolderOpen","alt","role","mb","mx","width","height","as","defaultProps","string","ascByFileableName","descByFileableName","ascByPosition","ascByFileablePosition","ascByAuthor","descByDate","MoveItemModal","isOpen","onClose","onMoveItem","dispatchFolders","sortedFolders","folderSorts","useState","reduce","fs","initialSelection","setInitialSelection","selection","setSelection","toggleFolderSelection","prevSelection","foldersIds","acc","fetchInitialSelection","newFolderIds","setNewFolderIds","currentFolders","filter","includes","newFolders","newFolderRef","useRef","namingNewFolder","setNamingNewFolder","isNameError","setIsNameError","handleNewFolder","newFolder","current","focus","handleFolderRename","updatedFolder","movePending","setMovePending","handleMove","Object","keys","Number","updatedSelection","length","shouldNotClose","x","Boolean","removeDisposedFolders","ids","tempFolder","find","forEach","cancelNewFolderBeforeClose","useDidMountEffect","Root","open","onOpenChange","Portal","Overlay","StyledContent","onPointerDownOutside","e","preventDefault","StyledTitle","fontSize","display","alignItems","p","my","borderRadius","icon","weight","color","pr","pt","maxHeight","overflowY","border","borderColor","newId","flexShrink","adaptive","checked","onChange","ml","defaultValue","placeholder","ref","autoComplete","onFocus","target","select","onKeyDown","keyCode","onBlur","trunclipse","pl","mt","justifyContent","secondary","onClick","disabled","mr","shape","number","func"],"mappings":"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","file":"package-folders.a55cfc67476f0b476f4c.chunk.js","sourcesContent":["export const sortTypes = {\n  ascending: 'ascending',\n  descending: 'descending',\n  manual: 'manual',\n  author: 'author',\n  date: 'date',\n}\n\nexport const itemTypes = {\n  course: 'Course',\n  flashcardCategory: 'FlashcardCategory',\n  flashcardSet: 'FlashcardSet',\n  folder: 'Folder',\n  lesson: 'Lesson',\n  post: 'Post',\n  video: 'Video',\n}\n","export default {\n  adjustFolderShares: 'adjust folder shares',\n  addPublishedFolder: 'add published folder',\n  createFolder: 'create folder',\n  deleteFolder: 'delete folder',\n  deleteFolderItem: 'delete folder item',\n  removeFolderShares: 'remove folder shares',\n  removePublishedFolder: 'remove published folder',\n  setCurrentFolderId: 'set current folder ID',\n  setFolders: 'set folders',\n  setNewPublished: 'set new published',\n  setNewShared: 'set new shared',\n  sortAscending: 'sort ascending',\n  sortDescending: 'sort descending',\n  sortManual: 'sort manual',\n  sortSharedAscending: 'sort shared ascending',\n  sortSharedAuthor: 'sort shared author',\n  sortSharedDate: 'sort shared date',\n  sortSharedDescending: 'sort shared descending',\n  updateChangedFolders: 'update changed folders',\n  updateFolder: 'update folder',\n}\n","import { sortTypes } from '../constants'\n\nimport actionTypes from './actionTypes'\n\n// For setting initial folder state\n// and for reordering root level folders\nconst setFolders = folders => ({\n  type: actionTypes.setFolders,\n  payload: folders,\n})\n\nconst setCurrentFolderId = folderId => ({\n  type: actionTypes.setCurrentFolderId,\n  payload: folderId,\n})\n\n// FOLDER actionTypes\nconst createFolder = folder => ({\n  type: actionTypes.createFolder,\n  payload: folder,\n})\n\nconst updateFolder = ({ id, name, description }) => ({\n  type: actionTypes.updateFolder,\n  payload: {\n    id,\n    name,\n    description,\n  },\n})\n\nconst addPublishedFolder = folder => ({\n  type: actionTypes.addPublishedFolder,\n  payload: folder,\n})\n\nconst removePublishedFolder = folderId => ({\n  type: actionTypes.removePublishedFolder,\n  payload: folderId,\n})\n\nconst adjustFolderShares = folder => ({\n  type: actionTypes.adjustFolderShares,\n  payload: folder,\n})\n\nconst removeFolderShares = folderId => ({\n  type: actionTypes.removeFolderShares,\n  payload: folderId,\n})\n\nconst deleteFolder = folderId => ({\n  type: actionTypes.deleteFolder,\n  payload: folderId,\n})\n\n// FOLDER ITEM actionTypes\n\n// When moving folder items, the endpoint responds with an array\n// of folders which changed as a result of the move\nconst updateChangedFolders = changedFolders => ({\n  type: actionTypes.updateChangedFolders,\n  payload: changedFolders,\n})\n\nconst deleteItem = ({ folderId, itemId }) => ({\n  type: actionTypes.deleteFolderItem,\n  payload: {\n    folderId,\n    itemId,\n  },\n})\n\n// UI\nconst setNewPublished = bool => ({\n  type: actionTypes.setNewPublished,\n  payload: bool,\n})\n\nconst setNewShared = bool => ({\n  type: actionTypes.setNewShared,\n  payload: bool,\n})\n\nconst sortAscending = () => ({\n  type: actionTypes.sortAscending,\n  payload: sortTypes.ascending,\n})\n\nconst sortDescending = () => ({\n  type: actionTypes.sortDescending,\n  payload: sortTypes.descending,\n})\n\nconst sortManual = () => ({\n  type: actionTypes.sortManual,\n  payload: sortTypes.manual,\n})\n\nconst sortSharedAscending = () => ({\n  type: actionTypes.sortSharedAscending,\n  payload: sortTypes.ascending,\n})\n\nconst sortSharedAuthor = () => ({\n  type: actionTypes.sortSharedAuthor,\n  payload: sortTypes.author,\n})\n\nconst sortSharedDate = () => ({\n  type: actionTypes.sortSharedDate,\n  payload: sortTypes.date,\n})\n\nconst sortSharedDescending = () => ({\n  type: actionTypes.sortSharedDescending,\n  payload: sortTypes.descending,\n})\n\nexport default {\n  addPublishedFolder,\n  adjustFolderShares,\n  createFolder,\n  deleteFolder,\n  deleteItem,\n  removeFolderShares,\n  removePublishedFolder,\n  setCurrentFolderId,\n  setFolders,\n  setNewPublished,\n  setNewShared,\n  sortAscending,\n  sortDescending,\n  sortManual,\n  sortSharedAscending,\n  sortSharedAuthor,\n  sortSharedDate,\n  sortSharedDescending,\n  updateChangedFolders,\n  updateFolder,\n}\n","import R from 'ramda'\n\n/**\n * Update a folder's name and/or description\n * @param {Object} folder - Information about the folder to be updated\n * @param {number} folder.id - The ID of the folder to be updated\n * @param {string} folder.name - The updated name to apply\n * @param {string} folder.description - The updated description to apply\n * @param {Array} folders - The array of folders from the current state\n */\nexport default function({ id, name, description }, folders) {\n  const index = R.findIndex(R.propEq('id', id), folders)\n\n  // findIndex returns -1 if index not found\n  if (index < 0) throw new Error(`updateFolder: id ${id} not found in array`)\n\n  // Folder at `index` passed as second arg to R.mergeLeft\n  return R.adjust(index, R.mergeLeft({ name, description }), folders)\n}\n","import R from 'ramda'\n\n/**\n * Remove a folder of `id` from a list of folders\n * @param {number} id - The id of the folder to delete\n * @param {Array} folders - The array of folders from the current state\n */\nexport default function withoutFolderId(id, folders) {\n  const folder = R.find(R.propEq('id', id), folders)\n\n  if (!folder) throw new Error(`Folder with id ${id} not found in folders`)\n\n  return R.without([folder], folders)\n}\n","import R from 'ramda'\n\n/**\n * Remove a folder item of `itemId` from a folder of `folderId` from a list of folders\n * @param {Object} item - Information about the item to be removed\n * @param {number} item.folderId - The ID of the folder the item should be removed from\n * @param {number} item.itemId - The ID of the item to be removed\n * @param {Array} folders - The array of folders from the current state\n */\nexport default function withoutFolderItem({ folderId, itemId }, folders) {\n  const folderIndex = R.findIndex(R.propEq('id', folderId), folders)\n  if (folderIndex < 0)\n    throw new Error(`deleteFolderItem: id ${folderId} not found in array`)\n\n  const { folderItems } = folders[folderIndex]\n\n  const itemIndex = R.findIndex(R.propEq('id', itemId), folderItems)\n  if (itemIndex < 0)\n    throw new Error(\n      `deleteFolderItem: id ${itemId} not found in [${folderId}].folderItems`\n    )\n\n  const itemToRemove = R.nth(itemIndex, folderItems)\n\n  return R.adjust(\n    folderIndex,\n    R.mergeLeft({ folderItems: R.without([itemToRemove], folderItems) }),\n    folders\n  )\n}\n","import R from 'ramda'\n\n/**\n * Mark a folder in myFolders as published\n * @param {Object} config\n * @param {number} config.folderId - the ID of the folder to be updated\n * @param {string} config.eslPublishedAt - the datestring from the folder's eslPublishedAt field\n * @param {Object[]} config.myFolders - the current myFolders array from the application state\n */\nexport function withFolderPublished({ folderId, eslPublishedAt, myFolders }) {\n  const index = R.findIndex(R.propEq('id', folderId), myFolders)\n\n  if (index < 0)\n    throw new Error(\n      `withPublishedFolder: Folder with id ${folderId} not found in myFolders`\n    )\n\n  return R.adjust(index, R.mergeLeft({ eslPublishedAt }), myFolders)\n}\n\n/**\n * Mark a folder in myFolders as not published\n * @param {Object} config\n * @param {number} config.folderId - the ID of the folder to be updated\n * @param {Object[]} config.myFolders - the current myFolders array from the application state\n */\nexport function withFolderUnpublished({ folderId, myFolders }) {\n  const index = R.findIndex(R.propEq('id', folderId), myFolders)\n\n  if (index < 0)\n    throw new Error(\n      `withPublishedFolder: Folder with id ${folderId} not found in myFolders`\n    )\n\n  return R.adjust(index, R.mergeLeft({ eslPublishedAt: null }), myFolders)\n}\n\n/**\n * Add a folder to the publishedFolders array\n * @param {Object} config\n * @param {Object} config.folder - the folder to add\n * @param {Object[]} config.publishedFolders - the current publishedFolders array from the application state\n */\nexport function addPublishedFolder({ folder, publishedFolders }) {\n  return R.append(folder, publishedFolders)\n}\n\n/**\n * Remove a folder from the publishedFolders array\n * @param {Object} config\n * @param {number} config.folderId - the ID of the folder to be removed\n * @param {Object[]} config.publishedFolders - the current publishedFolders array from the application state\n */\nexport function removePublishedFolder({ folderId, publishedFolders }) {\n  const folder = R.find(R.propEq('id', folderId), publishedFolders)\n\n  if (!folder)\n    throw new Error(\n      `removePublishedFolder: Folder with id ${folderId} not found in publishedFolders`\n    )\n\n  return R.without([folder], publishedFolders)\n}\n","import R from 'ramda'\n\n// Update a folder's sharedSchools and/or sharedUsers arrays\nexport function adjustFolderShares({ folder, myFolders }) {\n  const existingFolderIndex = R.findIndex(R.propEq('id', folder.id), myFolders)\n\n  if (existingFolderIndex < 0) {\n    throw new Error(\n      `updateSharedFolder: Folder with id ${folder.id} not found in myFolders`\n    )\n  }\n\n  const { sharedSchools, sharedUsers } = folder\n\n  return R.adjust(\n    existingFolderIndex,\n    R.mergeLeft({ sharedSchools, sharedUsers }),\n    myFolders\n  )\n}\n\n// Remove any school and/or user IDs from a folder's sharedSchools and sharedUsers arrays\nexport function removeFolderShares({ folderId, myFolders }) {\n  const existingFolderIndex = R.findIndex(R.propEq('id', folderId), myFolders)\n\n  if (existingFolderIndex < 0) {\n    throw new Error(\n      `removeFolderShares: Folder with id ${folderId} not found in myFolders`\n    )\n  }\n\n  return R.adjust(\n    existingFolderIndex,\n    R.mergeLeft({ sharedSchools: [], sharedUsers: [] }),\n    myFolders\n  )\n}\n","import R from 'ramda'\n\nimport actionTypes from './actionTypes'\n\nimport addNewFolder from './helpers/addNewFolder'\nimport adjustFolder from './helpers/adjustFolder'\nimport replaceChangedFolders from './helpers/replaceChangedFolders'\nimport withoutFolderId from './helpers/withoutFolderId'\nimport withoutFolderItem from './helpers/withoutFolderItem'\nimport {\n  addPublishedFolder,\n  removePublishedFolder,\n  withFolderPublished,\n  withFolderUnpublished,\n} from './helpers/publishedFolders'\nimport { adjustFolderShares, removeFolderShares } from './helpers/sharedFolders'\n\nexport default function foldersReducer(state, action) {\n  const { type, payload } = action\n\n  return R.cond([\n    [\n      R.equals(actionTypes.setFolders),\n      () => ({ ...state, myFolders: payload }),\n    ],\n    [\n      R.equals(actionTypes.setCurrentFolderId),\n      () => ({ ...state, currentFolderId: payload }),\n    ],\n    [\n      R.equals(actionTypes.createFolder),\n      () => ({\n        ...state,\n        myFolders: addNewFolder(payload, state.myFolders),\n      }),\n    ],\n    [\n      R.equals(actionTypes.updateFolder),\n      () => ({\n        ...state,\n        myFolders: adjustFolder(\n          {\n            id: payload.id,\n            name: payload.name,\n            description: payload.description,\n          },\n          state.myFolders\n        ),\n      }),\n    ],\n    [\n      R.equals(actionTypes.updateChangedFolders),\n      () => ({\n        ...state,\n        myFolders: replaceChangedFolders({\n          changedFolders: payload,\n          folders: state.myFolders,\n        }),\n      }),\n    ],\n    [\n      R.equals(actionTypes.addPublishedFolder),\n      () => ({\n        ...state,\n        myFolders: withFolderPublished({\n          folderId: payload.id,\n          eslPublishedAt: payload.eslPublishedAt,\n          myFolders: state.myFolders,\n        }),\n        publishedFolders: addPublishedFolder({\n          folder: payload,\n          publishedFolders: state.publishedFolders,\n        }),\n      }),\n    ],\n    [\n      R.equals(actionTypes.removePublishedFolder),\n      () => ({\n        ...state,\n        myFolders: withFolderUnpublished({\n          folderId: payload,\n          myFolders: state.myFolders,\n        }),\n        publishedFolders: removePublishedFolder({\n          folderId: payload,\n          publishedFolders: state.publishedFolders,\n        }),\n      }),\n    ],\n    [\n      R.equals(actionTypes.adjustFolderShares),\n      () => ({\n        ...state,\n        myFolders: adjustFolderShares({\n          folder: payload,\n          myFolders: state.myFolders,\n        }),\n      }),\n    ],\n    [\n      R.equals(actionTypes.removeFolderShares),\n      () => ({\n        ...state,\n        myFolders: removeFolderShares({\n          folderId: payload,\n          myFolders: state.myFolders,\n        }),\n      }),\n    ],\n    [\n      R.equals(actionTypes.deleteFolder),\n      () => ({\n        ...state,\n        myFolders: withoutFolderId(payload, state.myFolders),\n      }),\n    ],\n    [\n      R.equals(actionTypes.deleteFolderItem),\n      () => ({\n        ...state,\n        myFolders: withoutFolderItem(\n          {\n            folderId: payload.folderId,\n            itemId: payload.itemId,\n          },\n          state.myFolders\n        ),\n      }),\n    ],\n    [\n      R.equals(actionTypes.setNewPublished),\n      () => ({\n        ...state,\n        hasNewPublished: payload,\n      }),\n    ],\n    [\n      R.equals(actionTypes.setNewShared),\n      () => ({\n        ...state,\n        hasNewShared: payload,\n      }),\n    ],\n    [\n      R.equals(actionTypes.sortAscending),\n      () => ({\n        ...state,\n        sort: payload,\n      }),\n    ],\n    [\n      R.equals(actionTypes.sortDescending),\n      () => ({\n        ...state,\n        sort: payload,\n      }),\n    ],\n    [\n      R.equals(actionTypes.sortManual),\n      () => ({\n        ...state,\n        sort: payload,\n      }),\n    ],\n    [\n      R.equals(actionTypes.sortSharedAscending),\n      () => ({\n        ...state,\n        sharedSort: payload,\n      }),\n    ],\n    [\n      R.equals(actionTypes.sortSharedAuthor),\n      () => ({\n        ...state,\n        sharedSort: payload,\n      }),\n    ],\n    [\n      R.equals(actionTypes.sortSharedDate),\n      () => ({\n        ...state,\n        sharedSort: payload,\n      }),\n    ],\n    [\n      R.equals(actionTypes.sortSharedDescending),\n      () => ({\n        ...state,\n        sharedSort: payload,\n      }),\n    ],\n    [\n      R.T,\n      () => {\n        throw new Error(`Unhandled action type: ${type}`)\n      },\n    ],\n  ])(type)\n}\n","import R from 'ramda'\n\n/**\n * Add a newly created folder to the user's folders\n * @param {Object} folder - The new folder returned by the endpoint\n * @param {Array} folders - The array of folders from the current state\n */\nexport default function addNewFolder(folder, folders) {\n  // Increment each existing folder's position (the new folder comes back with position: 1)\n  const repositionedFolders = folders.map(f => ({\n    ...f,\n    position: R.inc(f.position),\n  }))\n\n  // Prepend the new folder to the repositioned folders\n  // NB: It actually doesn't matter if we prepend or append the new folder to the folders array,\n  // since manual sorting looks at position properties and not array indexes.\n  // R.prepend is only used here to be thematically consistent.\n  return R.prepend(folder, repositionedFolders)\n}\n","import R from 'ramda'\n\n/**\n * Update multiple folders with new copies\n * (folder items endpoint returns all changed folders\n * when an item is moved to/from folder(s))\n * @param {Object} config - The configuration object\n * @param {Array} config.changedFolders - The list of changed folders as returned from the endpoint\n * @param {Array} folders - The array of folders from the current state\n */\nexport default function replaceChangedFolders({ changedFolders, folders }) {\n  // create a copy of the existing folders array\n  let mergedFolders = folders\n\n  const replaceFolder = changedFolder => {\n    // get the index of the changedFolder in mergedFolders\n    const indexFromMerged = R.findIndex(\n      R.propEq('id', changedFolder.id),\n      mergedFolders\n    )\n    if (indexFromMerged < 0)\n      throw new Error(\n        `replaceFolder: id ${changedFolder.id} not found in mergedFolders`\n      )\n\n    // replace the existing folder with the changedFolder\n    // yes, gross object mutation, I know (;__;)\n    mergedFolders = R.update(indexFromMerged, changedFolder, mergedFolders)\n  }\n\n  // Run replaceFolder for every folder in changedFolders\n  changedFolders.map(replaceFolder)\n\n  // return the gross mutated array\n  return mergedFolders\n}\n","export const foldersEndpoint = `/api/v1/folders`\nexport const folderItemsEndpoint = `/api/v1/folder_items`\n","import xhr from 'Common/utils/xhr'\nimport { foldersEndpoint } from './endpoints'\n\n/**\n * Create a folder\n * @param {Object} config\n * @param {string} [config.name=null] - The name of the new folder; endpoint returns 'Untitled Folder' by default\n * @param {string} [config.description=null] - The folder description; endpoint returns null by default\n * @param {(Object|Boolean)} [config.item=false] - A folder item object to instantiate the folder with\n */\nconst createFolder = ({ name = null, description = null, item = false } = {}) =>\n  xhr({\n    url: foldersEndpoint,\n    method: 'POST',\n    data: {\n      folder: {\n        name,\n        description,\n        ...(item && {\n          folder_items_attributes: [\n            {\n              fileable_id: item.fileable.id,\n              fileable_type: item.fileable.type,\n            },\n          ],\n        }),\n      },\n    },\n  })\n\nexport default createFolder\n","import copyFolder from './copyFolder'\nimport createFolder from './createFolder'\nimport deleteFolder from './deleteFolder'\nimport deleteItem from './deleteItem'\nimport getFolderContent from './getFolderContent'\nimport getFolders from './getFolders'\nimport getItemFolders from './getItemFolders'\nimport publishFolder from './publishFolder'\nimport putItem from './putItem'\nimport sendFolderNotification from './sendFolderNotification'\nimport shareFolder from './shareFolder'\nimport unpublishFolder from './unpublishFolder'\nimport unshareFolder from './unshareFolder'\nimport updateFolder from './updateFolder'\nimport updateFolderPosition from './updateFolderPosition'\nimport updateItemPosition from './updateItemPosition'\n\nexport default {\n  copyFolder,\n  createFolder,\n  deleteFolder,\n  deleteItem,\n  getFolderContent,\n  getFolders,\n  getItemFolders,\n  publishFolder,\n  putItem,\n  sendFolderNotification,\n  shareFolder,\n  unpublishFolder,\n  unshareFolder,\n  updateFolder,\n  updateFolderPosition,\n  updateItemPosition,\n}\n","import xhr from 'Common/utils/xhr'\nimport { foldersEndpoint } from './endpoints'\n\n/**\n * Copies a folder to the current user's own folders\n * @param {number} id - The ID of the folder to be copied\n */\nconst copyFolder = id =>\n  xhr({\n    url: `${foldersEndpoint}/${id}/copy`,\n    method: 'POST',\n  })\n\nexport default copyFolder\n","import xhr from 'Common/utils/xhr'\nimport { foldersEndpoint } from './endpoints'\n\nconst deleteFolder = id =>\n  xhr({\n    url: `${foldersEndpoint}/${id}`,\n    method: 'DELETE',\n  })\n\nexport default deleteFolder\n","import xhr from 'Common/utils/xhr'\nimport { folderItemsEndpoint } from './endpoints'\n\nconst deleteItem = itemId =>\n  xhr({\n    url: `${folderItemsEndpoint}/${itemId}`,\n    method: 'DELETE',\n  })\n\nexport default deleteItem\n","import xhr from 'Common/utils/xhr'\nimport { foldersEndpoint } from './endpoints'\n\nconst getFolderContent = id =>\n  xhr({\n    url: `${foldersEndpoint}/${id}`,\n    method: 'GET',\n  })\n\nexport default getFolderContent\n","import xhr from 'Common/utils/xhr'\nimport { foldersEndpoint } from './endpoints'\n\nconst getFolders = () =>\n  xhr({\n    url: foldersEndpoint,\n    method: 'GET',\n  })\n\nexport default getFolders\n","import xhr from 'Common/utils/xhr'\nimport { folderItemsEndpoint } from './endpoints'\n\nconst getItemFolders = ({ itemId, itemType }) =>\n  xhr({\n    url: `${folderItemsEndpoint}/check_item?id=${itemId}&type=${itemType}`,\n    method: 'GET',\n  })\n\nexport default getItemFolders\n","import xhr from 'Common/utils/xhr'\nimport { foldersEndpoint } from './endpoints'\n\n/**\n * Publish a folder (ESL Folders account only; makes it visible under Ellii Folders)\n * @param {Object} config\n * @param {number} config.id - The folder ID\n * @param {string} config.publishedAt - ISO datetime string to for when the folder was published\n */\nconst publishFolder = ({ id, publishedAt }) =>\n  xhr({\n    url: `${foldersEndpoint}/${id}`,\n    method: 'PUT',\n    data: {\n      folder: {\n        esl_published_at: publishedAt,\n      },\n    },\n  })\n\nexport default publishFolder\n","import xhr from 'Common/utils/xhr'\nimport { folderItemsEndpoint } from './endpoints'\n\n/**\n * Put an item into one or more folders\n * @param {Object} config\n * @param {number} config.fileableId - The fileable ID of the folder item\n * @param {string} config.fileableType - The fileable type of the folder item\n * @param {number[]} config.inFolders - The list of folder IDs to place the folder item in\n */\nconst putItem = ({ fileableId, fileableType, inFolders }) =>\n  xhr({\n    url: `${folderItemsEndpoint}/move`,\n    method: 'PUT',\n    data: {\n      folders: inFolders,\n      folder_item: {\n        fileable_id: fileableId,\n        fileable_type: fileableType,\n      },\n    },\n  })\n\nexport default putItem\n","import xhr from 'Common/utils/xhr'\nimport { foldersEndpoint } from './endpoints'\n\n/**\n * Triggers the creation of a folder notification\n * @param {number} id - The ID of the folder to create the notification\n */\nconst sendFolderNotification = id =>\n  xhr({\n    url: `${foldersEndpoint}/${id}/send_notification`,\n    method: 'POST',\n  })\n\nexport default sendFolderNotification\n","import xhr from 'Common/utils/xhr'\nimport { foldersEndpoint } from './endpoints'\n\n/**\n * Share a folder with a school, or with users. Presently only one school may be\n * shared with, but this may change in future (hence it being an array of school ids).\n * Note that if the schools array is populated, the users array should be empty,\n * and vice versa — you cannot share a folder both with a school and individual users.\n * Both arrays may be empty at once to clear any previous shares.\n * @param {Object} config\n * @param {number} config.id - The id of the folder to share\n * @param {number[]} config.schools - Array of schools ids to share the folder with\n * @param {numberp[]} config.users - Array of user ids to share the folder with\n */\nconst shareFolder = ({ id, schools, users }) =>\n  xhr({\n    url: `${foldersEndpoint}/${id}/share`,\n    method: 'POST',\n    data: {\n      schools,\n      users,\n    },\n  })\n\nexport default shareFolder\n","import xhr from 'Common/utils/xhr'\nimport { foldersEndpoint } from './endpoints'\n\n/**\n * Unpublish a folder (ESL Folders account only)\n * @param {number} id - The folder ID\n */\nconst unpublishFolder = id =>\n  xhr({\n    url: `${foldersEndpoint}/${id}`,\n    method: 'PUT',\n    data: {\n      folder: {\n        esl_published_at: null,\n      },\n    },\n  })\n\nexport default unpublishFolder\n","import xhr from 'Common/utils/xhr'\nimport { foldersEndpoint } from './endpoints'\n\n/**\n * Removes any sharing associations from a folder\n * @param {number} id - The id of the folder to unshare\n */\nconst unshareFolder = id =>\n  xhr({\n    url: `${foldersEndpoint}/${id}/unshare`,\n    method: 'DELETE',\n  })\n\nexport default unshareFolder\n","import xhr from 'Common/utils/xhr'\nimport { foldersEndpoint } from './endpoints'\n\n/**\n * Update a folder's name and/or description\n * @param {Object} config\n * @param {number} config.id - The folder ID\n * @param {string} config.name - The folder name to be used\n * @param {string} config.description - The folder description to be used\n */\nconst updateFolder = ({ id, name, description }) =>\n  xhr({\n    url: `${foldersEndpoint}/${id}`,\n    method: 'PUT',\n    data: {\n      folder: {\n        name,\n        description,\n      },\n    },\n  })\n\nexport default updateFolder\n","import xhr from 'Common/utils/xhr'\nimport { foldersEndpoint } from './endpoints'\n\n/**\n * Update a folder's position in the sort order\n * @param {Object} config\n * @param {number} config.id - The folder ID\n * @param {number} config.position - The new position for the folder; positions are 1-based\n */\nconst updateFolderPosition = ({ id, position }) =>\n  xhr({\n    url: `${foldersEndpoint}/${id}`,\n    method: 'PUT',\n    data: {\n      folder: {\n        position,\n      },\n    },\n  })\n\nexport default updateFolderPosition\n","import xhr from 'Common/utils/xhr'\nimport { folderItemsEndpoint } from './endpoints'\n\n/**\n * Update a folder item's position in the sort order\n * @param {Object} config\n * @param {number} config.id - The folder item ID\n * @param {number} config.position - The new position for the folder item; positions are 1-based\n */\nconst updateItemPosition = ({ id, position }) =>\n  xhr({\n    url: `${folderItemsEndpoint}/${id}`,\n    method: 'PUT',\n    data: {\n      folder_item: {\n        position,\n      },\n    },\n  })\n\nexport default updateItemPosition\n","import PropTypes from 'prop-types'\nimport R from 'ramda'\nimport { createContext, useContext, useEffect, useReducer } from 'react'\n\nimport getInitialState from 'Common/utils/getInitialState'\nimport useUserAccess from 'Common/hooks/useUserAccess'\n\nimport actions from './actions'\nimport foldersReducer from './reducer'\nimport services from '../services'\n\n// Context objects\nconst FoldersState = createContext()\nconst FoldersDispatch = createContext()\n\nFoldersState.displayName = 'FoldersState'\nFoldersDispatch.displayName = 'FoldersDispatch'\n\n// Context hook\nfunction useFoldersContext() {\n  const state = useContext(FoldersState)\n  const dispatch = useContext(FoldersDispatch)\n\n  if (state === undefined || dispatch === undefined) {\n    throw new Error('useFoldersContext must be used within a FoldersProvider')\n  }\n\n  return [state, dispatch]\n}\n\nfunction FoldersProvider({ children }) {\n  const {\n    myFolders,\n    publishedFolders,\n    sharedFolders,\n    school,\n    users,\n  } = getInitialState()\n\n  const initialState = {\n    myFolders,\n    publishedFolders,\n    sharedFolders,\n    school,\n    users,\n    currentFolderId: null, // Will be updated by WatchCurrentFolder in Folders app; otherwise not used\n    hasNewPublished: false,\n    hasNewShared: false,\n    isPublisher: R.any(R.has('eslPublishedAt'), myFolders),\n    sort: 'manual',\n    sharedSort: 'ascending',\n  }\n\n  const [foldersState, setFoldersState] = useReducer(\n    foldersReducer,\n    initialState\n  )\n\n  return (\n    <FoldersState.Provider value={foldersState}>\n      <FoldersDispatch.Provider value={setFoldersState}>\n        {children}\n      </FoldersDispatch.Provider>\n    </FoldersState.Provider>\n  )\n}\n\nFoldersProvider.propTypes = {\n  children: PropTypes.node.isRequired,\n}\n\n// Context provider for the MoveItemModal used on content pages with API-based initial state\n// `fallback` node will be rendered until folders have been fetched\nfunction ModalFoldersProvider({ fallback, children }) {\n  // [1]\n  // We need to store the initial state as a variable so we can compare referential equality (see [2]).\n  // (This avoids the use of a useState just for tracking whether the fetch has completed or not.)\n  const initialFolders = []\n  const { userLoggedIn } = useUserAccess()\n\n  const [foldersState, setFoldersState] = useReducer(foldersReducer, {\n    myFolders: initialFolders,\n    userLoggedIn,\n  })\n\n  // Fetch the user's folders when the Provider first mounts\n  useEffect(() => {\n    async function fetchFolders() {\n      const { data: fetchedFolders } = await services.getFolders()\n      return setFoldersState(actions.setFolders(fetchedFolders))\n    }\n\n    userLoggedIn && fetchFolders()\n  }, [userLoggedIn])\n\n  // [2]\n  // If the folders haven't been fetched yet, display the fallback.\n  // If the state is referentially equal to our initial empty state, the fetch hasn't been completed yet.\n  if (foldersState.myFolders === initialFolders) {\n    return fallback\n  }\n\n  return (\n    <FoldersState.Provider value={foldersState}>\n      <FoldersDispatch.Provider value={setFoldersState}>\n        {children}\n      </FoldersDispatch.Provider>\n    </FoldersState.Provider>\n  )\n}\n\nModalFoldersProvider.propTypes = {\n  children: PropTypes.node.isRequired,\n  fallback: PropTypes.node.isRequired,\n}\n\nexport { FoldersProvider, ModalFoldersProvider, useFoldersContext }\n","import PropTypes from 'prop-types'\n\nimport FolderOpen from 'Images/folders/folder-open.svg'\nimport { Box, Image, Heading } from '@redriverpress/system'\n\nexport default function EmptyState({ action, message }) {\n  return (\n    <Box py={5} px={3} textAlign='center' bg='white'>\n      <Image\n        src={FolderOpen}\n        alt='Illustration of an open folder'\n        role='presentation'\n        mb={[3, null, 4]}\n        mx='auto'\n        width='230px'\n        height='auto'\n      />\n      <Heading as='h4' mb={0}>\n        {message}\n      </Heading>\n      {action && action}\n    </Box>\n  )\n}\n\nEmptyState.defaultProps = {\n  action: null,\n}\n\nEmptyState.propTypes = {\n  action: PropTypes.node,\n  message: PropTypes.string.isRequired,\n}\n","import R from 'ramda'\n\n// FILEABLE NAME\nconst ascByFileableName = R.ascend(R.path(['fileable', 'name']))\nconst descByFileableName = R.descend(R.path(['fileable', 'name']))\n\n// POSITIONS\nconst ascByPosition = R.ascend(R.prop('position'))\nconst ascByFileablePosition = R.ascend(R.path(['fileable', 'position']))\n\n// SHARED\n\n// Sort first by author name, then name of folder\nconst ascByAuthor = [R.ascend(R.prop('authorName')), R.ascend(R.prop('name'))]\n\n// Sort first by most recent date, then by name of folder\nconst descByDate = [R.descend(R.prop('sharedAt')), R.ascend(R.prop('name'))]\n\nexport default {\n  ascByFileableName,\n  descByFileableName,\n  ascByPosition,\n  ascByFileablePosition,\n  ascByAuthor,\n  descByDate,\n}\n","/**\n * This modal allows a user to select which of their folders the item should be in.\n * The folders it's already in will be selected by default; they can uncheck those folders to remove it.\n * They can also create a new folder in this modal, give it a name (default is 'Untitled Folder'),\n * and place the item in that folder (though the new folder will be saved whether they place the item\n * in that folder or not.)\n */\nimport PropTypes from 'prop-types'\nimport {\n  any,\n  assoc,\n  equals,\n  find,\n  last,\n  prepend,\n  propEq,\n  sort,\n  without,\n} from 'ramda'\nimport { Fragment, useEffect, useRef, useState } from 'react'\n\nimport {\n  Box,\n  Button,\n  CheckboxControl,\n  Dialog,\n  HR,\n  Heading,\n  Icon,\n  Image,\n  LoadingSpinner,\n  SimpleInput,\n  Text,\n} from '@redriverpress/system'\nimport FolderOpen from 'Images/folders/folder-open.svg'\nimport useDidMountEffect from 'Common/hooks/useDidMountEffect'\n\nimport folderSorts from '../utils/sort'\nimport services from '../services'\nimport { actions, useFoldersContext } from '../context'\n\n/* This component lends several patterns to SaveSelectionDialog.js.\n * Changes to one component may need to be reflected in the other.\n */\nexport default function MoveItemModal({ isOpen, onClose, item, onMoveItem }) {\n  const [{ myFolders }, dispatchFolders] = useFoldersContext()\n  const sortedFolders = sort(folderSorts.ascByPosition, myFolders)\n\n  // Initialize `selection` as an object, whose keys are the id of each folder,\n  // and whose value is true if that folder contains the item's fileable ID and type already\n  const [initialSelection, setInitialSelection] = useState(\n    sortedFolders.reduce((fs, f) => ({ ...fs, [f.id]: false }), {})\n  )\n\n  // Selection is an object whose keys are folder IDs\n  // and values are booleans indicating the check status of that folder\n  const [selection, setSelection] = useState(initialSelection)\n  const toggleFolderSelection = folderId =>\n    setSelection(assoc(folderId, !selection[folderId], selection))\n\n  useEffect(() => {\n    if (!isOpen) return\n\n    const fetchInitialSelection = async () => {\n      const { data } = await services.getItemFolders({\n        itemId: item.fileable.id,\n        itemType: item.fileable.type,\n      })\n\n      setInitialSelection(prevSelection => ({\n        ...prevSelection,\n        ...data.foldersIds.reduce((acc, folderId) => {\n          acc[folderId] = true\n          return acc\n        }, {}),\n      }))\n    }\n\n    fetchInitialSelection()\n    setSelection(initialSelection)\n  }, [isOpen])\n\n  useEffect(() => {\n    // Set the selection back to initialSelection whenever the modal opens/closes.\n    // The avoids unsaved changes in the selection persisting across opening/closing.\n    setSelection(initialSelection)\n  }, [isOpen, initialSelection])\n\n  // Array of IDs of new folders made since opening the modal\n  // (Persists across open/close, but not across rerenders)\n  const [newFolderIds, setNewFolderIds] = useState([])\n\n  // currentFolders are the sorted folders which existed when\n  // the MoveItemModal was last rendered (opening/closing doesn't rerender!)\n  const currentFolders = sortedFolders.filter(f => !newFolderIds.includes(f.id))\n\n  // newFolders is sortedFolders without currentFolders\n  const newFolders = without(currentFolders, myFolders)\n\n  const newFolderRef = useRef(null)\n  const [namingNewFolder, setNamingNewFolder] = useState(false)\n\n  const [isNameError, setIsNameError] = useState(false)\n\n  // When clicking the New Folder button:\n  // 1. Show a spinner in the New Folder button\n  // 2. Request new folder from BE\n  // 3. Put new folder in context\n  // 4. Add new folder's ID to newFolderIDs\n  // 5. Toggle state for naming new folder\n  // 6. Focus the new folder's name input (this step won't work on iOS)\n  const handleNewFolder = async () => {\n    const { data: newFolder } = await services.createFolder({\n      name: 'ellii_temp_default',\n    })\n    dispatchFolders(actions.createFolder(newFolder))\n    setNewFolderIds(current => prepend(newFolder.id, current))\n    setSelection({ ...selection, [newFolder.id]: true })\n    newFolderRef.current.focus()\n  }\n\n  const handleFolderRename = async ({ id, name }) => {\n    if (!name) return setIsNameError(true)\n\n    const { data: updatedFolder } = await services.updateFolder({\n      id,\n      name,\n      description: '',\n    })\n    dispatchFolders(actions.updateFolder(updatedFolder))\n    setNamingNewFolder(false)\n  }\n\n  const [movePending, setMovePending] = useState(false)\n  const handleMove = async () => {\n    if (namingNewFolder) return setIsNameError(true)\n    setMovePending(true)\n\n    // Filter folder IDs which map to `true` in `selection`\n    // to determine which folders the item should now be in\n    const inFolders = Object.keys(selection)\n      .filter(f => selection[f])\n      .map(id => Number(id))\n\n    const { data: changedFolders } = await services.putItem({\n      inFolders,\n      fileableId: item.fileable.id,\n      fileableType: item.fileable.type,\n    })\n\n    // Update selection based on the new folder data\n    const updatedSelection = changedFolders.reduce((acc, folder) => {\n      acc[folder.id] = false\n      return acc\n    }, {})\n\n    setMovePending(false)\n    dispatchFolders(actions.updateChangedFolders(changedFolders))\n\n    setSelection(updatedSelection)\n    setInitialSelection(updatedSelection)\n\n    onMoveItem && onMoveItem(!!inFolders.length)\n    setIsNameError(false)\n    onClose(!!newFolders.length)\n  }\n\n  // Don't allow modal to close when any of these conditions are true\n  const shouldNotClose = any(x => Boolean(x), [movePending, namingNewFolder])\n\n  const handleClose = () => {\n    if (isOpen || shouldNotClose) return\n\n    setSelection(initialSelection)\n    setNewFolderIds([])\n    setIsNameError(false)\n    onClose()\n  }\n\n  // If the user reloads the page or cancels\n  // the 'new folder' process instead of\n  // saving, this method will remove the folder\n  // that was created\n  const removeDisposedFolders = ids => {\n    const tempFolder = myFolders.find(\n      folder => folder.name === 'ellii_temp_default'\n    )\n    if (tempFolder) {\n      dispatchFolders(actions.deleteFolder(tempFolder.id))\n      setNewFolderIds(\n        newFolderIds.filter(folderId => tempFolder.id !== folderId)\n      )\n      services.deleteFolder(tempFolder.id)\n    } else if (ids?.length) {\n      ids.forEach(id => {\n        dispatchFolders(actions.deleteFolder(id))\n        setNewFolderIds(newFolderIds.filter(folderId => id !== folderId))\n        services.deleteFolder(id)\n      })\n    }\n  }\n\n  const cancelNewFolderBeforeClose = () => {\n    if (newFolders) {\n      removeDisposedFolders(newFolders.map(folder => folder.id))\n      setNamingNewFolder(false)\n      setIsNameError(false)\n      onClose(!!newFolders.length)\n    }\n  }\n\n  // only triggers 'handleNewFolder' if namingFolder is set 'true'\n  useEffect(() => {\n    if (namingNewFolder) {\n      handleNewFolder()\n    }\n  }, [namingNewFolder])\n\n  useDidMountEffect(() => {\n    if (!namingNewFolder) {\n      removeDisposedFolders()\n    }\n  }, [myFolders])\n\n  return (\n    <Dialog.Root\n      open={shouldNotClose || isOpen}\n      onOpenChange={cancelNewFolderBeforeClose}\n    >\n      <Dialog.Portal>\n        <Dialog.Overlay />\n        <Dialog.StyledContent\n          onPointerDownOutside={e => {\n            e.preventDefault()\n            if (!newFolders.length) handleClose()\n          }}\n        >\n          <Dialog.StyledTitle>\n            <Heading as='h6' mb={0}>\n              Save to…\n            </Heading>\n          </Dialog.StyledTitle>\n\n          {!myFolders.length ? (\n            <Box py={5} px={3} textAlign='center' bg='white'>\n              <Image\n                src={FolderOpen}\n                alt='Illustration of an open folder'\n                role='presentation'\n              />\n              <Heading as='h4' mb={0} mt={[3, null, 4]}>\n                You have no folders\n              </Heading>\n            </Box>\n          ) : (\n            <>\n              <Text fontSize={0}>\n                Check to add content or uncheck to remove content from\n                folder(s).\n              </Text>\n              {isNameError && (\n                <Box\n                  display='flex'\n                  alignItems='flex-start'\n                  p={2}\n                  my={[2, null, 3]}\n                  bg='red50'\n                  borderRadius='small'\n                >\n                  <Icon\n                    icon='exclamation-triangle'\n                    weight='solid'\n                    color='red700'\n                    fontSize={1}\n                    pr={2}\n                    pt={1}\n                  />\n                  <Text fontSize={0} color='red700'>\n                    Folder’s name can’t be blank.\n                  </Text>\n                </Box>\n              )}\n              <Box\n                my={[3, null, 4]}\n                maxHeight={['12rem', null, '15rem']}\n                overflowY='scroll'\n                borderRadius='rounded'\n                border='2px solid'\n                borderColor='gray150'\n              >\n                {/* List new folders before the rest of the folders */}\n                {newFolderIds.map(newId => {\n                  const folder = find(propEq('id', newId), newFolders)\n                  return (\n                    <Fragment key={folder.id}>\n                      <Box p={2} display='flex' alignItems='center'>\n                        <Box as='label' flexShrink={0}>\n                          <CheckboxControl\n                            adaptive\n                            name='folders'\n                            value={folder.id}\n                            checked={selection[folder.id]}\n                            onChange={() => toggleFolderSelection(folder.id)}\n                          />\n                        </Box>\n                        {/* Render an input if this folder is the most recent new folder\n                    and namingNewFolder is true */}\n                        {folder.id === newFolderIds[0] && namingNewFolder ? (\n                          <SimpleInput\n                            ml={2}\n                            defaultValue=''\n                            placeholder='Type folder name'\n                            ref={newFolderRef}\n                            autoComplete='off'\n                            onFocus={e => e.target.select()}\n                            onKeyDown={e =>\n                              e.keyCode === 13 &&\n                              handleFolderRename({\n                                id: folder.id,\n                                name: e.target.value,\n                              })\n                            }\n                            onBlur={e =>\n                              handleFolderRename({\n                                id: folder.id,\n                                name: e.target.value,\n                              })\n                            }\n                          />\n                        ) : (\n                          <Text trunclipse pl={2}>\n                            {folder.name}\n                          </Text>\n                        )}\n                      </Box>\n                      {/* Don't render an HR under this folder if it's the last one in the newFolders\n                    and there are no currentFolders (results in a double bottom border w/ wrapping box) */}\n                      {!currentFolders.length &&\n                      equals(folder, last(newFolders)) ? null : (\n                        <HR borderColor='gray150' />\n                      )}\n                    </Fragment>\n                  )\n                })}\n                {currentFolders.map(folder => (\n                  <Fragment key={folder.id}>\n                    <Box p={2} display='flex' alignItems='center'>\n                      <Box as='label' flexShrink={0}>\n                        <CheckboxControl\n                          adaptive\n                          name='folders'\n                          value={folder.id}\n                          checked={selection[folder.id]}\n                          onChange={() => toggleFolderSelection(folder.id)}\n                        />\n                      </Box>\n                      <Text trunclipse pl={2}>\n                        {folder.name}\n                      </Text>\n                    </Box>\n                    {!equals(folder, last(currentFolders)) && (\n                      <HR borderColor='gray150' />\n                    )}\n                  </Fragment>\n                ))}\n              </Box>\n            </>\n          )}\n\n          <Box display={['block', null, 'flex']} justifyContent='space-between'>\n            <Button\n              adaptive\n              secondary\n              width={[1, null, 'auto']}\n              mb={[3, null, 0]}\n              onClick={() => setNamingNewFolder(true)}\n              disabled={movePending || namingNewFolder}\n            >\n              <Text as='span' pl={2}>\n                New Folder\n              </Text>\n            </Button>\n\n            <Box width={[1, null, 'auto']} display='flex'>\n              <Button\n                adaptive\n                secondary\n                width={[1 / 2, null, 'auto']}\n                mr={2}\n                onClick={cancelNewFolderBeforeClose}\n              >\n                <Icon icon='times' pr={2} />\n                Cancel\n              </Button>\n\n              <Button\n                adaptive\n                width={[1 / 2, null, 'auto']}\n                onClick={handleMove}\n                disabled={\n                  equals(selection, initialSelection) ||\n                  movePending ||\n                  namingNewFolder\n                }\n              >\n                {movePending ? <LoadingSpinner /> : <Icon icon='check' />}\n                <Text as='span' pl={2}>\n                  Save\n                </Text>\n              </Button>\n            </Box>\n          </Box>\n        </Dialog.StyledContent>\n      </Dialog.Portal>\n    </Dialog.Root>\n  )\n}\n\nMoveItemModal.defaultProps = {\n  onMoveItem: null,\n}\n\nMoveItemModal.propTypes = {\n  isOpen: PropTypes.bool.isRequired,\n  item: PropTypes.shape({\n    fileable: PropTypes.shape({\n      id: PropTypes.number.isRequired,\n      type: PropTypes.string.isRequired,\n    }).isRequired,\n  }).isRequired,\n  onClose: PropTypes.func.isRequired,\n  onMoveItem: PropTypes.func,\n}\n"],"sourceRoot":""}