UNPKG

subtitles-editor

Version:

A React component for editing SRT and VTT subtitles directly in a textarea, styled with TailwindCSS.

617 lines (607 loc) 43.8 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) : typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.SubtitlesEditor = {}, global.React)); })(this, (function (exports, React) { 'use strict'; function styleInject(css, ref) { if (ref === void 0) ref = {}; var insertAt = ref.insertAt; if (typeof document === 'undefined') { return; } var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } var css_248z = "*, ::before, ::after {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}\n\n::backdrop {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}/*\n! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com\n*//*\n1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)\n2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)\n*/\n\n*,\n::before,\n::after {\n box-sizing: border-box; /* 1 */\n border-width: 0; /* 2 */\n border-style: solid; /* 2 */\n border-color: #e5e7eb; /* 2 */\n}\n\n::before,\n::after {\n --tw-content: '';\n}\n\n/*\n1. Use a consistent sensible line-height in all browsers.\n2. Prevent adjustments of font size after orientation changes in iOS.\n3. Use a more readable tab size.\n4. Use the user's configured `sans` font-family by default.\n5. Use the user's configured `sans` font-feature-settings by default.\n6. Use the user's configured `sans` font-variation-settings by default.\n7. Disable tap highlights on iOS\n*/\n\nhtml,\n:host {\n line-height: 1.5; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n -moz-tab-size: 4; /* 3 */\n -o-tab-size: 4;\n tab-size: 4; /* 3 */\n font-family: ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"; /* 4 */\n font-feature-settings: normal; /* 5 */\n font-variation-settings: normal; /* 6 */\n -webkit-tap-highlight-color: transparent; /* 7 */\n}\n\n/*\n1. Remove the margin in all browsers.\n2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.\n*/\n\nbody {\n margin: 0; /* 1 */\n line-height: inherit; /* 2 */\n}\n\n/*\n1. Add the correct height in Firefox.\n2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)\n3. Ensure horizontal rules are visible by default.\n*/\n\nhr {\n height: 0; /* 1 */\n color: inherit; /* 2 */\n border-top-width: 1px; /* 3 */\n}\n\n/*\nAdd the correct text decoration in Chrome, Edge, and Safari.\n*/\n\nabbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\n\n/*\nRemove the default font size and weight for headings.\n*/\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\n\n/*\nReset links to optimize for opt-in styling instead of opt-out.\n*/\n\na {\n color: inherit;\n text-decoration: inherit;\n}\n\n/*\nAdd the correct font weight in Edge and Safari.\n*/\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/*\n1. Use the user's configured `mono` font-family by default.\n2. Use the user's configured `mono` font-feature-settings by default.\n3. Use the user's configured `mono` font-variation-settings by default.\n4. Correct the odd `em` font sizing in all browsers.\n*/\n\ncode,\nkbd,\nsamp,\npre {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace; /* 1 */\n font-feature-settings: normal; /* 2 */\n font-variation-settings: normal; /* 3 */\n font-size: 1em; /* 4 */\n}\n\n/*\nAdd the correct font size in all browsers.\n*/\n\nsmall {\n font-size: 80%;\n}\n\n/*\nPrevent `sub` and `sup` elements from affecting the line height in all browsers.\n*/\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/*\n1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)\n2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)\n3. Remove gaps between table borders by default.\n*/\n\ntable {\n text-indent: 0; /* 1 */\n border-color: inherit; /* 2 */\n border-collapse: collapse; /* 3 */\n}\n\n/*\n1. Change the font styles in all browsers.\n2. Remove the margin in Firefox and Safari.\n3. Remove default padding in all browsers.\n*/\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-feature-settings: inherit; /* 1 */\n font-variation-settings: inherit; /* 1 */\n font-size: 100%; /* 1 */\n font-weight: inherit; /* 1 */\n line-height: inherit; /* 1 */\n letter-spacing: inherit; /* 1 */\n color: inherit; /* 1 */\n margin: 0; /* 2 */\n padding: 0; /* 3 */\n}\n\n/*\nRemove the inheritance of text transform in Edge and Firefox.\n*/\n\nbutton,\nselect {\n text-transform: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Remove default button styles.\n*/\n\nbutton,\ninput:where([type='button']),\ninput:where([type='reset']),\ninput:where([type='submit']) {\n -webkit-appearance: button; /* 1 */\n background-color: transparent; /* 2 */\n background-image: none; /* 2 */\n}\n\n/*\nUse the modern Firefox focus style for all focusable elements.\n*/\n\n:-moz-focusring {\n outline: auto;\n}\n\n/*\nRemove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)\n*/\n\n:-moz-ui-invalid {\n box-shadow: none;\n}\n\n/*\nAdd the correct vertical alignment in Chrome and Firefox.\n*/\n\nprogress {\n vertical-align: baseline;\n}\n\n/*\nCorrect the cursor style of increment and decrement buttons in Safari.\n*/\n\n::-webkit-inner-spin-button,\n::-webkit-outer-spin-button {\n height: auto;\n}\n\n/*\n1. Correct the odd appearance in Chrome and Safari.\n2. Correct the outline style in Safari.\n*/\n\n[type='search'] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/*\nRemove the inner padding in Chrome and Safari on macOS.\n*/\n\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Change font properties to `inherit` in Safari.\n*/\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/*\nAdd the correct display in Chrome and Safari.\n*/\n\nsummary {\n display: list-item;\n}\n\n/*\nRemoves the default spacing and border for appropriate elements.\n*/\n\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\n\nfieldset {\n margin: 0;\n padding: 0;\n}\n\nlegend {\n padding: 0;\n}\n\nol,\nul,\nmenu {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/*\nReset default styling for dialogs.\n*/\ndialog {\n padding: 0;\n}\n\n/*\nPrevent resizing textareas horizontally by default.\n*/\n\ntextarea {\n resize: vertical;\n}\n\n/*\n1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)\n2. Set the default placeholder color to the user's configured gray 400 color.\n*/\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n opacity: 1; /* 1 */\n color: #9ca3af; /* 2 */\n}\n\ninput::placeholder,\ntextarea::placeholder {\n opacity: 1; /* 1 */\n color: #9ca3af; /* 2 */\n}\n\n/*\nSet the default cursor for buttons.\n*/\n\nbutton,\n[role=\"button\"] {\n cursor: pointer;\n}\n\n/*\nMake sure disabled buttons don't get the pointer cursor.\n*/\n:disabled {\n cursor: default;\n}\n\n/*\n1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)\n2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)\n This can trigger a poorly considered lint error in some tools but is included by design.\n*/\n\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block; /* 1 */\n vertical-align: middle; /* 2 */\n}\n\n/*\nConstrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)\n*/\n\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n\n/* Make elements with the HTML hidden attribute stay hidden by default */\n[hidden]:where(:not([hidden=\"until-found\"])) {\n display: none;\n}\r\n.static {\n position: static;\n}\r\n.fixed {\n position: fixed;\n}\r\n.relative {\n position: relative;\n}\r\n.inset-0 {\n inset: 0px;\n}\r\n.z-10 {\n z-index: 10;\n}\r\n.mx-auto {\n margin-left: auto;\n margin-right: auto;\n}\r\n.mb-2 {\n margin-bottom: 0.5rem;\n}\r\n.mb-3 {\n margin-bottom: 0.75rem;\n}\r\n.mb-4 {\n margin-bottom: 1rem;\n}\r\n.mt-2 {\n margin-top: 0.5rem;\n}\r\n.mt-3 {\n margin-top: 0.75rem;\n}\r\n.flex {\n display: flex;\n}\r\n.inline-flex {\n display: inline-flex;\n}\r\n.grid {\n display: grid;\n}\r\n.hidden {\n display: none;\n}\r\n.size-12 {\n width: 3rem;\n height: 3rem;\n}\r\n.size-6 {\n width: 1.5rem;\n height: 1.5rem;\n}\r\n.max-h-\\[400px\\] {\n max-height: 400px;\n}\r\n.min-h-\\[400px\\] {\n min-height: 400px;\n}\r\n.min-h-full {\n min-height: 100%;\n}\r\n.w-full {\n width: 100%;\n}\r\n.w-screen {\n width: 100vw;\n}\r\n.shrink-0 {\n flex-shrink: 0;\n}\r\n.transform {\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\r\n.grid-cols-1 {\n grid-template-columns: repeat(1, minmax(0, 1fr));\n}\r\n.items-end {\n align-items: flex-end;\n}\r\n.items-center {\n align-items: center;\n}\r\n.justify-center {\n justify-content: center;\n}\r\n.justify-between {\n justify-content: space-between;\n}\r\n.gap-4 {\n gap: 1rem;\n}\r\n.space-x-2 > :not([hidden]) ~ :not([hidden]) {\n --tw-space-x-reverse: 0;\n margin-right: calc(0.5rem * var(--tw-space-x-reverse));\n margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));\n}\r\n.space-x-4 > :not([hidden]) ~ :not([hidden]) {\n --tw-space-x-reverse: 0;\n margin-right: calc(1rem * var(--tw-space-x-reverse));\n margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));\n}\r\n.overflow-hidden {\n overflow: hidden;\n}\r\n.overflow-y-auto {\n overflow-y: auto;\n}\r\n.rounded {\n border-radius: 0.25rem;\n}\r\n.rounded-full {\n border-radius: 9999px;\n}\r\n.rounded-lg {\n border-radius: 0.5rem;\n}\r\n.rounded-md {\n border-radius: 0.375rem;\n}\r\n.border {\n border-width: 1px;\n}\r\n.bg-blue-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(59 130 246 / var(--tw-bg-opacity, 1));\n}\r\n.bg-gray-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1));\n}\r\n.bg-gray-50 {\n --tw-bg-opacity: 1;\n background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1));\n}\r\n.bg-gray-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(107 114 128 / var(--tw-bg-opacity, 1));\n}\r\n.bg-gray-500\\/75 {\n background-color: rgb(107 114 128 / 0.75);\n}\r\n.bg-green-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(34 197 94 / var(--tw-bg-opacity, 1));\n}\r\n.bg-red-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(254 226 226 / var(--tw-bg-opacity, 1));\n}\r\n.bg-red-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(239 68 68 / var(--tw-bg-opacity, 1));\n}\r\n.bg-red-600 {\n --tw-bg-opacity: 1;\n background-color: rgb(220 38 38 / var(--tw-bg-opacity, 1));\n}\r\n.bg-white {\n --tw-bg-opacity: 1;\n background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));\n}\r\n.bg-yellow-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(234 179 8 / var(--tw-bg-opacity, 1));\n}\r\n.p-2 {\n padding: 0.5rem;\n}\r\n.p-3 {\n padding: 0.75rem;\n}\r\n.p-4 {\n padding: 1rem;\n}\r\n.px-3 {\n padding-left: 0.75rem;\n padding-right: 0.75rem;\n}\r\n.px-4 {\n padding-left: 1rem;\n padding-right: 1rem;\n}\r\n.py-1 {\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n}\r\n.py-2 {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\r\n.py-3 {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n}\r\n.pb-4 {\n padding-bottom: 1rem;\n}\r\n.pt-5 {\n padding-top: 1.25rem;\n}\r\n.text-left {\n text-align: left;\n}\r\n.text-center {\n text-align: center;\n}\r\n.text-base {\n font-size: 1rem;\n line-height: 1.5rem;\n}\r\n.text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n}\r\n.font-semibold {\n font-weight: 600;\n}\r\n.text-gray-500 {\n --tw-text-opacity: 1;\n color: rgb(107 114 128 / var(--tw-text-opacity, 1));\n}\r\n.text-gray-900 {\n --tw-text-opacity: 1;\n color: rgb(17 24 39 / var(--tw-text-opacity, 1));\n}\r\n.text-green-600 {\n --tw-text-opacity: 1;\n color: rgb(22 163 74 / var(--tw-text-opacity, 1));\n}\r\n.text-red-600 {\n --tw-text-opacity: 1;\n color: rgb(220 38 38 / var(--tw-text-opacity, 1));\n}\r\n.text-white {\n --tw-text-opacity: 1;\n color: rgb(255 255 255 / var(--tw-text-opacity, 1));\n}\r\n.shadow-md {\n --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\r\n.shadow-xl {\n --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\r\n.ring-1 {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);\n}\r\n.ring-inset {\n --tw-ring-inset: inset;\n}\r\n.ring-gray-300 {\n --tw-ring-opacity: 1;\n --tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity, 1));\n}\r\n.filter {\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\n}\r\n.transition-all {\n transition-property: all;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\r\n.transition-opacity {\n transition-property: opacity;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\r\n\r\n[contenteditable][placeholder]:empty:before {\r\n content: attr(placeholder);\r\n position: absolute;\r\n color: gray;\r\n background-color: transparent;\r\n}\r\n\r\n.hover\\:bg-gray-50:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1));\n}\r\n\r\n.hover\\:bg-red-500:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(239 68 68 / var(--tw-bg-opacity, 1));\n}\r\n\r\n@media (min-width: 640px) {\n\n .sm\\:mx-0 {\n margin-left: 0px;\n margin-right: 0px;\n }\n\n .sm\\:my-8 {\n margin-top: 2rem;\n margin-bottom: 2rem;\n }\n\n .sm\\:ml-3 {\n margin-left: 0.75rem;\n }\n\n .sm\\:ml-4 {\n margin-left: 1rem;\n }\n\n .sm\\:mt-0 {\n margin-top: 0px;\n }\n\n .sm\\:flex {\n display: flex;\n }\n\n .sm\\:size-10 {\n width: 2.5rem;\n height: 2.5rem;\n }\n\n .sm\\:w-auto {\n width: auto;\n }\n\n .sm\\:w-full {\n width: 100%;\n }\n\n .sm\\:max-w-lg {\n max-width: 32rem;\n }\n\n .sm\\:flex-row-reverse {\n flex-direction: row-reverse;\n }\n\n .sm\\:items-start {\n align-items: flex-start;\n }\n\n .sm\\:items-center {\n align-items: center;\n }\n\n .sm\\:p-0 {\n padding: 0px;\n }\n\n .sm\\:p-6 {\n padding: 1.5rem;\n }\n\n .sm\\:px-6 {\n padding-left: 1.5rem;\n padding-right: 1.5rem;\n }\n\n .sm\\:pb-4 {\n padding-bottom: 1rem;\n }\n\n .sm\\:text-left {\n text-align: left;\n }\n}\r\n\r\n@media (min-width: 1024px) {\n\n .lg\\:grid-cols-2 {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n}"; styleInject(css_248z); function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); } function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || false, o.configurable = true, "value" in o && (o.writable = true), Object.defineProperty(e, _toPropertyKey(o.key), o); } } function _createClass(e, r, t) { return t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: false }), e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e; } function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = true, o = false; try { if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = true, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread2(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), true).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function Dialog(_ref) { var onConfirm = _ref.onConfirm, onCancel = _ref.onCancel; return /*#__PURE__*/React.createElement("div", { className: "relative z-10", "aria-labelledby": "modal-title", role: "dialog", "aria-modal": "true" }, /*#__PURE__*/React.createElement("div", { className: "fixed inset-0 bg-gray-500/75 transition-opacity", "aria-hidden": "true" }), /*#__PURE__*/React.createElement("div", { className: "fixed inset-0 z-10 w-screen overflow-y-auto" }, /*#__PURE__*/React.createElement("div", { className: "flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0" }, /*#__PURE__*/React.createElement("div", { className: "relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg" }, /*#__PURE__*/React.createElement("div", { className: "bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4" }, /*#__PURE__*/React.createElement("div", { className: "sm:flex sm:items-start" }, /*#__PURE__*/React.createElement("div", { className: "mx-auto flex size-12 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:size-10" }, /*#__PURE__*/React.createElement("svg", { className: "size-6 text-red-600", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", "aria-hidden": "true", "data-slot": "icon" }, /*#__PURE__*/React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z" }))), /*#__PURE__*/React.createElement("div", { className: "mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left" }, /*#__PURE__*/React.createElement("h3", { className: "text-base font-semibold text-gray-900", id: "modal-title" }, "Confirm deletion."), /*#__PURE__*/React.createElement("div", { className: "mt-2" }, /*#__PURE__*/React.createElement("p", { className: "text-sm text-gray-500" }, "Are you sure you want to delete this subtitle? This action is irreversible."))))), /*#__PURE__*/React.createElement("div", { className: "flex justify-between bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6" }, /*#__PURE__*/React.createElement("button", { onClick: onConfirm, type: "button", className: "inline-flex w-full justify-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-red-500 sm:ml-3 sm:w-auto" }, "Delete"), /*#__PURE__*/React.createElement("button", { onClick: onCancel, type: "button", className: "mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50 sm:mt-0 sm:w-auto" }, "Cancel")))))); } function ContentEditable(_ref) { var className = _ref.className, value = _ref.value, onChange = _ref.onChange, placeholder = _ref.placeholder; var contentEditableRef = React.useRef(null); React.useEffect(function () { if (contentEditableRef.current && contentEditableRef.current.innerText !== value) { contentEditableRef.current.innerText = value; } }, [value]); var handleInput = function handleInput(event) { var newValue = event.currentTarget.innerText.trim(); if (newValue === '' || newValue === '<br>') { onChange(''); } else { onChange(newValue); } }; return /*#__PURE__*/React.createElement("div", { className: className, contentEditable: true, ref: contentEditableRef, onInput: handleInput, placeholder: placeholder, role: "textbox" }); } function SubtitlesEditor(_ref) { var subtitles = _ref.subtitles, onChange = _ref.onChange, _ref$editable = _ref.editable, editable = _ref$editable === void 0 ? true : _ref$editable, _ref$placeholderSubti = _ref.placeholderSubtitle, placeholderSubtitle = _ref$placeholderSubti === void 0 ? "Typing here..." : _ref$placeholderSubti, _ref$addSubtitleButto = _ref.addSubtitleButton, addSubtitleButton = _ref$addSubtitleButto === void 0 ? false : _ref$addSubtitleButto, onAddSubtitle = _ref.onAddSubtitle, _ref$deleteSubtitleBu = _ref.deleteSubtitleButton, deleteSubtitleButton = _ref$deleteSubtitleBu === void 0 ? false : _ref$deleteSubtitleBu, _ref$timingEditable = _ref.timingEditable, timingEditable = _ref$timingEditable === void 0 ? false : _ref$timingEditable, onSave = _ref.onSave, _ref$downloadEnabled = _ref.downloadEnabled, downloadEnabled = _ref$downloadEnabled === void 0 ? false : _ref$downloadEnabled, _ref$undoEnabled = _ref.undoEnabled, undoEnabled = _ref$undoEnabled === void 0 ? true : _ref$undoEnabled, _ref$redoEnabled = _ref.redoEnabled, redoEnabled = _ref$redoEnabled === void 0 ? true : _ref$redoEnabled, _ref$buttons = _ref.buttons, buttons = _ref$buttons === void 0 ? { addSubtitle: { text: "Add a subtitle.", className: "" }, save: { text: "Save", className: "" }, download: { text: "Download", className: "" }, "delete": { text: "Delete", className: "" }, insertTimecode: { text: "Insert timecode", className: "" }, undo: { className: "" }, redo: { className: "" } } : _ref$buttons, undoButton = _ref.undoButton, redoButton = _ref.redoButton; var _useState = React.useState(subtitles), _useState2 = _slicedToArray(_useState, 2), editedSubtitles = _useState2[0], setEditedSubtitles = _useState2[1]; var _useState3 = React.useState(false), _useState4 = _slicedToArray(_useState3, 2), isDialogOpen = _useState4[0], setIsDialogOpen = _useState4[1]; var _useState5 = React.useState(null), _useState6 = _slicedToArray(_useState5, 2), subtitleToDelete = _useState6[0], setSubtitleToDelete = _useState6[1]; var _useState7 = React.useState(''), _useState8 = _slicedToArray(_useState7, 2), notification = _useState8[0], setNotification = _useState8[1]; var _useState9 = React.useState(function () { return subtitles.map(function () { return { history: [], index: -1 }; }); }), _useState10 = _slicedToArray(_useState9, 2), subtitlesHistory = _useState10[0], setSubtitlesHistory = _useState10[1]; var fileInputRef = React.useRef(null); var defaultButtonClass = "px-4 py-2 text-white rounded"; var getButtonClass = React.useCallback(function (userClass) { return userClass ? userClass : defaultButtonClass; }, []); var handleSubtitleChange = React.useCallback(function (index, field, value) { setEditedSubtitles(function (prevSubtitles) { var newSubtitles = _toConsumableArray(prevSubtitles); newSubtitles[index] = _objectSpread2(_objectSpread2({}, newSubtitles[index]), {}, _defineProperty({}, field, value)); saveHistory(index, newSubtitles); onChange === null || onChange === void 0 || onChange(newSubtitles); return newSubtitles; }); }, [onChange]); var saveHistory = React.useCallback(function (index, newSubtitles) { setSubtitlesHistory(function (prevHistory) { var updatedHistory = _toConsumableArray(prevHistory); if (!updatedHistory[index]) { updatedHistory[index] = { history: [], index: -1 }; } updatedHistory[index].history = [].concat(_toConsumableArray(updatedHistory[index].history.slice(0, updatedHistory[index].index + 1)), [newSubtitles[index]]); updatedHistory[index].index += 1; return updatedHistory; }); }, []); var handleHistoryNavigation = React.useCallback(function (index, direction) { setSubtitlesHistory(function (prevHistory) { var updatedHistory = _toConsumableArray(prevHistory); var subtitleHistory = updatedHistory[index]; if (!subtitleHistory) return prevHistory; var newIndex = subtitleHistory.index + direction; if (newIndex >= 0 && newIndex < subtitleHistory.history.length) { setEditedSubtitles(function (prev) { var updatedSubtitles = _toConsumableArray(prev); updatedSubtitles[index] = subtitleHistory.history[newIndex]; return updatedSubtitles; }); subtitleHistory.index = newIndex; } return updatedHistory; }); }, []); var handleAddSubtitle = function handleAddSubtitle() { var lastSubtitle = editedSubtitles[editedSubtitles.length - 1] || {}; var newSubtitle = { text: "", start: lastSubtitle.end || "", end: "" }; setEditedSubtitles([].concat(_toConsumableArray(editedSubtitles), [newSubtitle])); var newSubtitlesHistory = _toConsumableArray(subtitlesHistory); if (!newSubtitlesHistory[editedSubtitles.length]) { newSubtitlesHistory.push({ history: [], index: -1 }); } saveHistory(editedSubtitles.length, [].concat(_toConsumableArray(editedSubtitles), [newSubtitle])); setSubtitlesHistory(newSubtitlesHistory); if (onAddSubtitle) onAddSubtitle(newSubtitle); }; var handleSave = function handleSave() { if (onSave) { onSave(editedSubtitles); showNotification('Subtitles saved successfully.'); } }; var handleDeleteSubtitle = function handleDeleteSubtitle(index) { setSubtitleToDelete(index); setIsDialogOpen(true); }; var confirmDelete = function confirmDelete() { var newSubtitles = editedSubtitles.filter(function (_, i) { return i !== subtitleToDelete; }); setEditedSubtitles(newSubtitles); saveHistory(subtitleToDelete, newSubtitles); setIsDialogOpen(false); setSubtitleToDelete(null); showNotification('Subtitle deleted.'); }; var cancelDelete = function cancelDelete() { setIsDialogOpen(false); setSubtitleToDelete(null); }; var formatTime = function formatTime() { var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ""; var parts = time.split(":"); if (parts.length === 3) { var _parts$2$split = parts[2].split("."), _parts$2$split2 = _slicedToArray(_parts$2$split, 2), seconds = _parts$2$split2[0], _parts$2$split2$ = _parts$2$split2[1], milliseconds = _parts$2$split2$ === void 0 ? "000" : _parts$2$split2$; return "".concat(parts[0], ":").concat(parts[1], ":").concat(seconds, ".").concat(milliseconds.padEnd(3, "0")); } return "00:00:00.000"; }; var insertTimecode = function insertTimecode(index) { setEditedSubtitles(function (prev) { var updatedSubtitles = _toConsumableArray(prev); var subtitle = updatedSubtitles[index]; var timecode = "\n".concat(formatTime(subtitle.start), " --> ").concat(formatTime(subtitle.end)); updatedSubtitles[index] = _objectSpread2(_objectSpread2({}, subtitle), {}, { text: "".concat(subtitle.text, "\n").concat(timecode) }); return updatedSubtitles; }); }; var handleDownload = function handleDownload() { if (!downloadEnabled) return; editedSubtitles.forEach(function (subtitle, index) { var subtitleText = "".concat(subtitle.text, "\n\n"); var blob = new Blob([subtitleText], { type: 'text/plain' }); var link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = "subtitle_".concat(index + 1, ".vtt"); link.click(); }); }; var showNotification = function showNotification(message) { setNotification(message); setTimeout(function () { return setNotification(''); }, 3000); }; var handleFileUpload = function handleFileUpload(index, event) { var file = event.target.files[0]; if (!file) return; var reader = new FileReader(); reader.onload = function () { var fileContent = reader.result; addContentToSubtitle(index, fileContent); }; reader.readAsText(file); event.target.value = null; }; var addContentToSubtitle = function addContentToSubtitle(index, content) { var newSubtitles = _toConsumableArray(editedSubtitles); if (newSubtitles[index]) { newSubtitles[index] = _objectSpread2(_objectSpread2({}, newSubtitles[index]), {}, { text: newSubtitles[index].text + content }); setEditedSubtitles(newSubtitles); if (onChange) onChange(newSubtitles); showNotification("Content of the file added to the subtitle."); } else { console.error("Subtitle not found at index.", index); } }; React.useEffect(function () { if (!Array.isArray(subtitles)) { console.error("Subtitles must be an array."); return; } var validSubtitles = subtitles.map(function (subtitle) { return { text: subtitle.text || "", start: subtitle.start || "", end: subtitle.end || "" }; }); setEditedSubtitles(validSubtitles); }, [subtitles]); return /*#__PURE__*/React.createElement("div", { className: "p-4 bg-gray-100 rounded-lg shadow-md" }, /*#__PURE__*/React.createElement("div", { className: "mb-4 flex justify-between" }, /*#__PURE__*/React.createElement("div", null, notification && /*#__PURE__*/React.createElement("div", { className: "text-green-600" }, notification))), /*#__PURE__*/React.createElement("div", { className: "grid grid-cols-1 gap-4 lg:grid-cols-2" }, editedSubtitles.map(function (subtitle, index) { return /*#__PURE__*/React.createElement("div", { key: index, className: "mb-3 p-3 border rounded bg-white" }, timingEditable && /*#__PURE__*/React.createElement("div", { className: "flex space-x-2 mb-2 items-center" }, /*#__PURE__*/React.createElement("input", { type: "time", step: "1", className: "p-2 border rounded", value: subtitle.start || "00:00:00", onChange: function onChange(e) { return handleSubtitleChange(index, 'start', e.target.value); }, disabled: !timingEditable }), /*#__PURE__*/React.createElement("input", { type: "time", step: "1", className: "p-2 border rounded", value: subtitle.end || "00:00:00", onChange: function onChange(e) { return handleSubtitleChange(index, 'end', e.target.value); }, disabled: !timingEditable }), /*#__PURE__*/React.createElement("button", { onClick: function onClick() { return insertTimecode(index); }, className: "px-3 py-1 bg-gray-500 text-white rounded" }, buttons.insertTimecode.text)), subtitle && /*#__PURE__*/React.createElement(ContentEditable, { className: "editor w-full min-h-[400px] max-h-[400px] p-2 border rounded bg-white overflow-y-auto", value: subtitle.text, onChange: function onChange(newValue) { return handleSubtitleChange(index, "text", newValue); }, placeholder: placeholderSubtitle }), deleteSubtitleButton && editable && /*#__PURE__*/React.createElement("div", { className: "flex justify-between mt-2" }, /*#__PURE__*/React.createElement("button", { onClick: function onClick() { return handleDeleteSubtitle(index); }, className: getButtonClass(buttons["delete"].className) + " bg-red-500" }, buttons["delete"].text), /*#__PURE__*/React.createElement("button", { onClick: function onClick() { return fileInputRef.current.click(); }, className: getButtonClass(buttons.insertTimecode.className) + " bg-gray-500" }, "Upload subtitles"), /*#__PURE__*/React.createElement("input", { key: index, ref: fileInputRef, type: "file", accept: ".srt, .vtt", onChange: function onChange(event) { return handleFileUpload(index, event); }, className: "hidden" }), /*#__PURE__*/React.createElement("div", { className: "flex space-x-4" }, undoEnabled && undoButton && /*#__PURE__*/React.createElement("div", { onClick: function onClick() { return handleHistoryNavigation(index, -1); }, className: buttons.undo.className }, undoButton), redoEnabled && redoButton && /*#__PURE__*/React.createElement("div", { onClick: function onClick() { return handleHistoryNavigation(index, 1); }, className: buttons.redo.className }, redoButton)))); })), /*#__PURE__*/React.createElement("div", { className: "flex justify-between" }, addSubtitleButton && editable && /*#__PURE__*/React.createElement("button", { onClick: handleAddSubtitle, className: getButtonClass(buttons.addSubtitle.className) + " bg-blue-500" }, buttons.addSubtitle.text), onSave && /*#__PURE__*/React.createElement("button", { onClick: handleSave, className: getButtonClass(buttons.save.className) + " bg-green-500" }, buttons.save.text), downloadEnabled && /*#__PURE__*/React.createElement("button", { onClick: handleDownload, className: getButtonClass(buttons.download.className) + " bg-yellow-500" }, buttons.download.text)), isDialogOpen && /*#__PURE__*/React.createElement(Dialog, { onConfirm: confirmDelete, onCancel: cancelDelete })); } var Subtitle = /*#__PURE__*/function () { function Subtitle() { var content = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; _classCallCheck(this, Subtitle); this.content = content; } return _createClass(Subtitle, null, [{ key: "convertSrtToVtt", value: function convertSrtToVtt(srtContent) { var vttContent = "WEBVTT\n\n"; var vttText = srtContent.replace(/(\d{2}:\d{2}:\d{2}),(\d{3}) --> (\d{2}:\d{2}:\d{2}),(\d{3})/g, '$1.$2 --> $3.$4'); return vttContent + vttText.trim(); } }, { key: "convertVttToSrt", value: function convertVttToSrt(vttContent) { var srtContent = vttContent.replace(/^WEBVTT[\r\n]*\s*/g, ""); var srtText = srtContent.replace(/(\d{2}:\d{2}:\d{2})\.(\d{3}) --> (\d{2}:\d{2}:\d{2})\.(\d{3})/g, function (match, p1, p2, p3, p4) { return "".concat(p1, ":").concat(p2, " --> ").concat(p3, ":").concat(p4); }); var srtWithNumbers = srtText.replace(/(?:\d{2}:\d{2}:\d{2}:\d{3}) --> /g, function (match, index) { var indexCount = 1; return "".concat(indexCount++, "\n"); }); return srtWithNumbers.trim(); } }, { key: "removeHtml", value: function removeHtml(text) { return text.replace(/<[^>]*>/g, ''); } }]); }(); exports.Subtitle = Subtitle; exports.SubtitlesEditor = SubtitlesEditor; }));