Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added examples/vue/public/sample.docx
Binary file not shown.
239 changes: 203 additions & 36 deletions examples/vue/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,59 +1,226 @@
<template>
<div class="app">
<header class="header">
<h1>DOCX Editor — Vue Example</h1>
<p class="subtitle">
This is a scaffold for the Vue.js integration of <code>@eigenpal/docx-core</code>.
The editor component is not yet implemented.
</p>
</header>
<main class="content">
<div class="placeholder">
<p>Vue editor component coming soon.</p>
<p>
See
<a href="https://github.com/eigenpal/docx-editor" target="_blank" rel="noopener">
the repository
</a>
for contribution guidelines.
</p>
<div class="header-left">
<h1 class="title">DOCX Editor — Vue</h1>
</div>
<div class="header-center">
<span v-if="fileName" class="file-name">{{ fileName }}</span>
</div>
<div class="header-right">
<label class="btn btn-primary">
<input
type="file"
accept=".docx"
@change="handleFileSelect"
class="file-input"
/>
Open DOCX
</label>
<button class="btn" @click="handleNew">New</button>
<button class="btn" @click="handleSave">Save</button>
<span v-if="status" class="status">{{ status }}</span>
</div>
</header>

<main class="main">
<DocxEditorVue
ref="editorRef"
:document-buffer="documentBuffer"
:document="currentDocument"
:show-toolbar="true"
@change="handleDocumentChange"
@error="handleError"
@ready="handleReady"
/>
</main>
</div>
</template>

<script setup lang="ts">
// Future: import { DocxEditor } from '@eigenpal/docx-editor-vue';
import { ref, onMounted } from 'vue';
import { DocxEditorVue } from '@eigenpal/docx-editor-vue';
import { createEmptyDocument } from '@eigenpal/docx-core';
import type { Document } from '@eigenpal/docx-core/types/document';

const editorRef = ref<InstanceType<typeof DocxEditorVue> | null>(null);
const documentBuffer = ref<ArrayBuffer | null>(null);
const currentDocument = ref<Document | null>(null);
const fileName = ref('sample.docx');
const status = ref('');

// Load sample document on mount
onMounted(async () => {
try {
const res = await fetch('/sample.docx');
const buffer = await res.arrayBuffer();
documentBuffer.value = buffer;
fileName.value = 'sample.docx';
} catch {
currentDocument.value = createEmptyDocument();
fileName.value = 'Untitled.docx';
}
});

function handleFileSelect(event: Event) {
const input = event.target as HTMLInputElement;
const file = input.files?.[0];
if (!file) return;

status.value = 'Loading...';
file.arrayBuffer().then((buffer) => {
currentDocument.value = null;
documentBuffer.value = buffer;
fileName.value = file.name;
status.value = '';
}).catch(() => {
status.value = 'Error loading file';
});
}

function handleNew() {
documentBuffer.value = null;
currentDocument.value = createEmptyDocument();
fileName.value = 'Untitled.docx';
status.value = '';
}

async function handleSave() {
if (!editorRef.value) return;

try {
status.value = 'Saving...';
const blob = await editorRef.value.save();
if (blob) {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName.value || 'document.docx';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
status.value = 'Saved!';
setTimeout(() => { status.value = ''; }, 2000);
}
} catch {
status.value = 'Save failed';
}
}

function handleDocumentChange(_doc: Document) {
// no-op — could track dirty state here
}

function handleError(error: Error) {
console.error('Editor error:', error);
status.value = `Error: ${error.message}`;
}

function handleReady() {
console.log('Editor ready');
}
</script>

<style scoped>
<style>
.app {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
background: #f8fafc;
}

.header {
margin-bottom: 2rem;
display: flex;
align-items: center;
padding: 8px 16px;
gap: 12px;
background: #fff;
border-bottom: 1px solid #e2e8f0;
}

.header-left {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.header h1 {
font-size: 1.5rem;
margin-bottom: 0.5rem;

.title {
font-size: 14px;
font-weight: 600;
color: #0f172a;
margin: 0;
}

.header-center {
flex: 1;
display: flex;
justify-content: center;
}
.subtitle {

.header-right {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}

.file-name {
font-size: 13px;
color: #64748b;
font-size: 0.875rem;
padding: 4px 10px;
background: #f1f5f9;
border-radius: 6px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 200px;
}
.content {
background: white;
border-radius: 8px;
padding: 3rem;
text-align: center;

.btn {
padding: 6px 12px;
background: #fff;
border: 1px solid #e2e8f0;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
color: #334155;
white-space: nowrap;
}
.placeholder {
color: #94a3b8;

.btn:hover {
background: #f1f5f9;
}
.placeholder a {
color: #3b82f6;

.btn-primary {
background: #0f172a;
color: #fff;
border-color: #0f172a;
cursor: pointer;
}

.btn-primary:hover {
background: #1e293b;
}

.file-input {
display: none;
}

.status {
font-size: 12px;
color: #64748b;
padding: 4px 8px;
background: #f1f5f9;
border-radius: 4px;
}

.main {
flex: 1;
display: flex;
overflow: hidden;
}
</style>
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"dev:nextjs": "cd examples/nextjs && npm run dev",
"dev:remix": "cd examples/remix && npm run dev",
"dev:astro": "cd examples/astro && npm run dev",
"dev:vue": "cd examples/vue && bun run dev",
"dev:demo": "bash examples/dev-all.sh",
"build": "bun run --filter '@eigenpal/docx-core' build && bun run --filter '@eigenpal/docx-js-editor' build",
"build:demo": "vite build --config examples/vite/vite.config.ts",
Expand Down Expand Up @@ -47,6 +48,7 @@
"@typescript-eslint/eslint-plugin": "^8.54.0",
"@typescript-eslint/parser": "^8.54.0",
"@vitejs/plugin-react": "^5.1.2",
"@vitejs/plugin-vue": "^6.0.4",
"autoprefixer": "^10.4.17",
"class-variance-authority": "^0.7.0",
"eslint": "^10.0.3",
Expand All @@ -63,7 +65,8 @@
"tailwindcss-animate": "^1.0.7",
"tsup": "^8.0.1",
"typescript": "^5.3.3",
"vite": "^7.3.1"
"vite": "^7.3.1",
"vue": "^3.5.29"
},
"keywords": [
"docx",
Expand Down
Loading