Design system.
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 

145 rader
5.5 KiB

  1. import * as React from 'react';
  2. import {formatFileSize, formatNumeral} from 'packages/web-kitchensink-reactnext/src/utils/numeral';
  3. import {useFileMetadata, useFileUrl} from 'src/index';
  4. import {augmentTextFile, getMimeTypeDescription} from 'packages/web-kitchensink-reactnext/src/utils/blob';
  5. import clsx from 'clsx';
  6. import {KeyValueTable} from 'categories/information/react';
  7. import {Refractor} from 'packages/web-kitchensink-reactnext/src/packages/react-refractor';
  8. import {useClientSide} from 'packages/react-utils';
  9. import type {CommonPreviewProps} from '../../../../../categories/blob/react/src/components/FileSelectBox';
  10. type TextFilePreviewDerivedComponent = HTMLDivElement;
  11. export interface TextFilePreviewProps<F extends Partial<File> = Partial<File>>
  12. extends React.HTMLProps<TextFilePreviewDerivedComponent>, CommonPreviewProps<F> {}
  13. export const TextFilePreview = React.forwardRef<TextFilePreviewDerivedComponent, TextFilePreviewProps>(({
  14. file,
  15. className,
  16. style,
  17. enhanced: enhancedProp = false,
  18. ...etcProps
  19. }, forwardedRef) => {
  20. const { fileWithUrl } = useFileUrl({ file });
  21. const { fileWithMetadata, error } = useFileMetadata({
  22. file: fileWithUrl,
  23. augmentFunction: augmentTextFile,
  24. });
  25. const { clientSide } = useClientSide({ clientSide: enhancedProp });
  26. if (!fileWithMetadata) {
  27. return null;
  28. }
  29. return (
  30. <div
  31. className={clsx(
  32. 'flex flex-col sm:grid sm:grid-cols-3 gap-8 w-full',
  33. className,
  34. )}
  35. style={style}
  36. >
  37. <div className="h-full relative col-span-2">
  38. <div className="absolute top-0 left-0 w-full h-full">
  39. {
  40. typeof fileWithMetadata.metadata?.contents === 'string'
  41. && (
  42. <div
  43. {...etcProps}
  44. data-testid="preview"
  45. role="presentation"
  46. className="w-full h-full select-none overflow-hidden text-xs"
  47. ref={forwardedRef}
  48. key={`${fileWithMetadata.url}:${fileWithMetadata.type}`}
  49. >
  50. <Refractor
  51. code={fileWithMetadata.metadata.contents}
  52. language={fileWithMetadata.metadata.scheme}
  53. lineNumbers={Boolean(fileWithMetadata.metadata.scheme)}
  54. />
  55. </div>
  56. )
  57. }
  58. </div>
  59. </div>
  60. <div className="flex-shrink-0 m-0 flex flex-col gap-4 justify-between">
  61. <KeyValueTable
  62. hiddenKeys
  63. data-testid="infoBox"
  64. properties={[
  65. Boolean(fileWithMetadata.name) && {
  66. key: 'Name',
  67. className: 'font-bold',
  68. valueProps: {
  69. children: fileWithMetadata.name,
  70. title: fileWithMetadata.name,
  71. },
  72. },
  73. (clientSide && Boolean(getMimeTypeDescription(fileWithMetadata.type, fileWithMetadata.name) || '(Loading)') || Boolean(fileWithMetadata.type)) && {
  74. key: 'Type',
  75. valueProps: {
  76. children: typeof fileWithMetadata.metadata?.schemeTitle === 'string' ? `${fileWithMetadata.metadata.schemeTitle} Source` : 'Text File',
  77. },
  78. },
  79. (clientSide && Boolean(formatFileSize(fileWithMetadata.size) || '(Loading)') || Boolean(fileWithMetadata.size)) && {
  80. key: 'Size',
  81. valueProps: {
  82. className: clsx(
  83. !formatFileSize(fileWithMetadata.size) && 'opacity-50'
  84. ),
  85. title: `${formatNumeral(fileWithMetadata.size ?? 0)} byte(s)`,
  86. children: formatFileSize(fileWithMetadata.size) || '(Loading)',
  87. },
  88. },
  89. typeof fileWithMetadata.metadata?.lineCount === 'number'
  90. && typeof fileWithMetadata.metadata?.linesOfCode !== 'number'
  91. && {
  92. key: 'Lines',
  93. valueProps: {
  94. children: `${formatNumeral(fileWithMetadata.metadata.lineCount)} line(s)`
  95. },
  96. },
  97. typeof fileWithMetadata.metadata?.lineCount === 'number'
  98. && typeof fileWithMetadata.metadata?.linesOfCode === 'number'
  99. && {
  100. key: 'Lines',
  101. valueProps: {
  102. children: `${formatNumeral(fileWithMetadata.metadata.lineCount)} line(s), ${formatNumeral(fileWithMetadata.metadata.linesOfCode)} loc`,
  103. },
  104. },
  105. Array.isArray(fileWithMetadata.metadata?.languageMatches)
  106. && {
  107. key: 'Language',
  108. valueProps: {
  109. children: (
  110. <dl
  111. className="text-sm"
  112. >
  113. {fileWithMetadata.metadata!.languageMatches.slice(0, 3).map(([language, probability], index) => {
  114. return (
  115. <div key={index} className="flex justify-between">
  116. <dt>
  117. {language.slice(0, 1).toUpperCase()}
  118. {language.slice(1)}
  119. </dt>
  120. <dd className="tabular-nums">
  121. {(probability * 100).toFixed(3)}%
  122. {' '}
  123. <abbr title="probability">prob.</abbr>
  124. </dd>
  125. </div>
  126. );
  127. })}
  128. </dl>
  129. ),
  130. },
  131. },
  132. ]}
  133. />
  134. </div>
  135. </div>
  136. );
  137. });
  138. TextFilePreview.displayName = 'TextFilePreview';