This commit is contained in:
Suphonchai Phoonsawat 2026-08-18 12:52:40 +07:00
commit 41573b3c4f
54 changed files with 32839 additions and 0 deletions

8
addin/.eslintrc.json Normal file
View file

@ -0,0 +1,8 @@
{
"plugins": [
"office-addins"
],
"extends": [
"plugin:office-addins/react"
]
}

8
addin/.hintrc Normal file
View file

@ -0,0 +1,8 @@
{
"extends": [
"development"
],
"hints": {
"typescript-config/strict": "off"
}
}

14
addin/.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,14 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"ms-edgedevtools.vscode-edge-devtools",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"msoffice.microsoft-office-add-in-debugger"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": []
}

26
addin/.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,26 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Word Desktop (Edge Chromium)",
"type": "msedge",
"request": "attach",
"port": 9229,
"timeout": 600000,
"webRoot": "${workspaceRoot}",
"preLaunchTask": "Debug: Word Desktop",
"postDebugTask": "Stop Debug"
},
{
"name": "Word Desktop (Edge Legacy)",
"type": "office-addin",
"request": "attach",
"url": "https://localhost:3000/taskpane.html?_host_Info=Word$Win32$16.01$en-US$$$$0",
"port": 9222,
"timeout": 600000,
"webRoot": "${workspaceRoot}",
"preLaunchTask": "Debug: Word Desktop",
"postDebugTask": "Stop Debug"
}
]
}

8
addin/.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,8 @@
{
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript"
]
}

160
addin/.vscode/tasks.json vendored Normal file
View file

@ -0,0 +1,160 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Build (Development)",
"type": "npm",
"script": "build:dev",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"clear": true,
"panel": "shared",
"showReuseMessage": false
}
},
{
"label": "Build (Production)",
"type": "npm",
"script": "build",
"group": "build",
"presentation": {
"clear": true,
"panel": "shared",
"showReuseMessage": false
}
},
{
"label": "Debug: Excel Desktop",
"type": "shell",
"command": "npm",
"args": [
"run",
"start",
"--",
"desktop",
"--app",
"excel"
],
"presentation": {
"clear": true,
"panel": "dedicated"
},
"problemMatcher": []
},
{
"label": "Debug: Outlook Desktop",
"type": "shell",
"command": "npm",
"args": [
"run",
"start",
"--",
"desktop",
"--app",
"outlook"
],
"presentation": {
"clear": true,
"panel": "dedicated"
},
"problemMatcher": []
},
{
"label": "Debug: PowerPoint Desktop",
"type": "shell",
"command": "npm",
"args": [
"run",
"start",
"--",
"desktop",
"--app",
"powerpoint"
],
"presentation": {
"clear": true,
"panel": "dedicated"
},
"problemMatcher": []
},
{
"label": "Debug: Word Desktop",
"type": "shell",
"command": "npm",
"args": [
"run",
"start",
"--",
"desktop",
"--app",
"word"
],
"presentation": {
"clear": true,
"panel": "dedicated"
},
"problemMatcher": []
},
{
"label": "Dev Server",
"type": "npm",
"script": "dev-server",
"presentation": {
"clear": true,
"panel": "dedicated"
},
"problemMatcher": []
},
{
"label": "Install",
"type": "npm",
"script": "install",
"presentation": {
"clear": true,
"panel": "shared",
"showReuseMessage": false
},
"problemMatcher": []
},
{
"label": "Lint: Check for problems",
"type": "npm",
"script": "lint",
"problemMatcher": [
"$eslint-stylish"
]
},
{
"label": "Lint: Fix all auto-fixable problems",
"type": "npm",
"script": "lint:fix",
"problemMatcher": [
"$eslint-stylish"
]
},
{
"label": "Stop Debug",
"type": "npm",
"script": "stop",
"presentation": {
"clear": true,
"panel": "shared",
"showReuseMessage": false
},
"problemMatcher": []
},
{
"label": "Watch",
"type": "npm",
"script": "watch",
"presentation": {
"clear": true,
"panel": "dedicated"
},
"problemMatcher": []
}
]
}

BIN
addin/assets/icon-128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
addin/assets/icon-16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
addin/assets/icon-32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
addin/assets/icon-64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
addin/assets/icon-80.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

13
addin/babel.config.json Normal file
View file

@ -0,0 +1,13 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"esmodules": false
}
}
],
"@babel/preset-typescript"
]
}

143
addin/manifest.json Normal file
View file

@ -0,0 +1,143 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/teams/v1.17/MicrosoftTeams.schema.json",
"id": "f2b103f1-1ab1-4e1b-8f0b-072aa3d4e19d",
"manifestVersion": "1.17",
"version": "1.0.0",
"name": {
"short": "Contoso Task Pane Add-in",
"full": "Contoso Task Pane Add-in"
},
"description": {
"short": "A template to get started.",
"full": "This is the template to get started."
},
"developer": {
"name": "Contoso",
"websiteUrl": "https://www.contoso.com",
"privacyUrl": "https://www.contoso.com/privacy",
"termsOfUseUrl": "https://www.contoso.com/servicesagreement"
},
"icons": {
"outline": "assets/outline.png",
"color": "assets/color.png"
},
"accentColor": "#230201",
"localizationInfo": {
"defaultLanguageTag": "en-us",
"additionalLanguages": []
},
"authorization": {
"permissions": {
"resourceSpecific": [
{
"name": "Mailbox.ReadWrite.User",
"type": "Delegated"
}
]
}
},
"validDomains": ["contoso.com"],
"extensions": [
{
"requirements": {
"scopes": ["mail"],
"capabilities": [
{ "name": "Mailbox", "minVersion": "1.3" }
]
},
"runtimes": [
{
"requirements": {
"capabilities": [
{ "name": "Mailbox", "minVersion": "1.3" }
]
},
"id": "TaskPaneRuntime",
"type": "general",
"code": {
"page": "https://localhost:3000/taskpane.html"
},
"lifetime": "short",
"actions": [
{
"id": "TaskPaneRuntimeShow",
"type":"openPage",
"pinnable": false,
"view": "dashboard"
}
]
},
{
"id": "CommandsRuntime",
"type": "general",
"code": {
"page": "https://localhost:3000/commands.html",
"script": "https://localhost:3000/commands.js"
},
"lifetime": "short",
"actions": [
{
"id": "action",
"type": "executeFunction"
}
]
}
],
"ribbons": [
{
"contexts": [
"mailCompose"
],
"tabs": [
{
"builtInTabId": "TabDefault",
"groups": [
{
"id": "msgComposeGroup",
"label": "Contoso Add-in",
"icons": [
{ "size": 16, "url": "https://localhost:3000/assets/icon-16.png" },
{ "size": 32, "url": "https://localhost:3000/assets/icon-32.png" },
{ "size": 80, "url": "https://localhost:3000/assets/icon-80.png" }
],
"controls": [
{
"id": "msgComposeOpenPaneButton",
"type": "button",
"label": "Show Task Pane",
"icons": [
{ "size": 16, "url": "https://localhost:3000/assets/icon-16.png" },
{ "size": 32, "url": "https://localhost:3000/assets/icon-32.png" },
{ "size": 80, "url": "https://localhost:3000/assets/icon-80.png" }
],
"supertip": {
"title": "Show Task Pane",
"description": "Opens a task pane."
},
"actionId": "TaskPaneRuntimeShow"
},
{
"id": "ActionButton",
"type": "button",
"label": "Perform an action",
"icons": [
{ "size": 16, "url": "https://localhost:3000/assets/icon-16.png" },
{ "size": 32, "url": "https://localhost:3000/assets/icon-32.png" },
{ "size": 80, "url": "https://localhost:3000/assets/icon-80.png" }
],
"supertip": {
"title": "Perform an action",
"description": "Perform an action when clicked."
},
"actionId": "action"
}
]
}
]
}
]
}
]
}
]
}

85
addin/manifest.xml Normal file
View file

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="TaskPaneApp">
<Id>4a1e184a-ab31-4fa4-b3c7-9a2283051c3f</Id>
<Version>1.0.0.0</Version>
<ProviderName>Contoso</ProviderName>
<DefaultLocale>en-US</DefaultLocale>
<DisplayName DefaultValue="addin"/>
<Description DefaultValue="A template to get started."/>
<IconUrl DefaultValue="https://localhost:3000/assets/icon-32.png"/>
<HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-64.png"/>
<SupportUrl DefaultValue="https://www.contoso.com/help"/>
<AppDomains>
<AppDomain>https://www.contoso.com</AppDomain>
</AppDomains>
<Hosts>
<Host Name="Document"/>
</Hosts>
<DefaultSettings>
<SourceLocation DefaultValue="https://localhost:3000/taskpane.html"/>
</DefaultSettings>
<Permissions>ReadWriteDocument</Permissions>
<VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0">
<Hosts>
<Host xsi:type="Document">
<DesktopFormFactor>
<GetStarted>
<Title resid="GetStarted.Title"/>
<Description resid="GetStarted.Description"/>
<LearnMoreUrl resid="GetStarted.LearnMoreUrl"/>
</GetStarted>
<FunctionFile resid="Commands.Url"/>
<ExtensionPoint xsi:type="PrimaryCommandSurface">
<OfficeTab id="TabHome">
<Group id="CommandsGroup">
<Label resid="CommandsGroup.Label"/>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Control xsi:type="Button" id="TaskpaneButton">
<Label resid="TaskpaneButton.Label"/>
<Supertip>
<Title resid="TaskpaneButton.Label"/>
<Description resid="TaskpaneButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ShowTaskpane">
<TaskpaneId>ButtonId1</TaskpaneId>
<SourceLocation resid="Taskpane.Url"/>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/>
<bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/>
<bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812"/>
<bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html"/>
<bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="GetStarted.Title" DefaultValue="Get started with your sample add-in!"/>
<bt:String id="CommandsGroup.Label" DefaultValue="Commands Group"/>
<bt:String id="TaskpaneButton.Label" DefaultValue="Show Task Pane"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="GetStarted.Description" DefaultValue="Your sample add-in loaded successfully. Go to the HOME tab and click the 'Show Task Pane' button to get started."/>
<bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Task Pane"/>
</bt:LongStrings>
</Resources>
</VersionOverrides>
</OfficeApp>

19178
addin/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

76
addin/package.json Normal file
View file

@ -0,0 +1,76 @@
{
"name": "office-addin-taskpane-react",
"version": "0.0.1",
"repository": {
"type": "git",
"url": "https://github.com/OfficeDev/Office-Addin-TaskPane-React.git"
},
"license": "MIT",
"config": {
"app_to_debug": "word",
"app_type_to_debug": "desktop",
"dev_server_port": 3000
},
"scripts": {
"build": "webpack --mode production",
"build:dev": "webpack --mode development",
"dev-server": "webpack serve --mode development",
"lint": "office-addin-lint check",
"lint:fix": "office-addin-lint fix",
"prettier": "office-addin-lint prettier",
"signin": "office-addin-dev-settings m365-account login",
"signout": "office-addin-dev-settings m365-account logout",
"start": "office-addin-debugging start manifest.xml",
"stop": "office-addin-debugging stop manifest.xml",
"validate": "office-addin-manifest validate manifest.xml",
"watch": "webpack --mode development --watch"
},
"dependencies": {
"@fluentui/react-components": "^9.55.1",
"@fluentui/react-icons": "^2.0.264",
"core-js": "^3.36.0",
"es6-promise": "^4.2.8",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"regenerator-runtime": "^0.14.1"
},
"devDependencies": {
"@babel/core": "^8.0.1",
"@babel/preset-env": "^8.0.2",
"@babel/preset-typescript": "^8.0.1",
"@types/office-js": "^1.0.377",
"@types/office-runtime": "^1.0.35",
"@types/react": "^18.2.64",
"@types/react-dom": "^18.2.21",
"@types/webpack": "^5.28.5",
"acorn": "^8.11.3",
"babel-loader": "^10.1.1",
"copy-webpack-plugin": "^14.0.0",
"eslint-plugin-office-addins": "^4.0.10",
"eslint-plugin-react": "^7.28.0",
"file-loader": "^6.2.0",
"html-loader": "^5.0.0",
"html-webpack-plugin": "^5.6.0",
"less": "^4.2.0",
"less-loader": "^12.2.0",
"office-addin-cli": "^2.0.10",
"office-addin-debugging": "^6.1.2",
"office-addin-dev-certs": "^2.0.10",
"office-addin-lint": "^3.0.10",
"office-addin-manifest": "^2.1.6",
"office-addin-prettier-config": "^2.0.5",
"os-browserify": "^0.3.0",
"process": "^0.11.10",
"source-map-loader": "^5.0.0",
"ts-loader": "^9.5.1",
"typescript": "^5.4.2",
"webpack": "^5.105.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^6.0.0"
},
"prettier": "office-addin-prettier-config",
"browserslist": [
"last 2 versions",
"ie 11"
]
}

View file

@ -0,0 +1,19 @@
@@ -1,18 +0,0 @@
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<!-- Office JavaScript API -->
<script type="text/javascript" src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
* See LICENSE in the project root for license information.
*/
/* global Office */
Office.onReady(() => {
// If needed, Office.js is ready to be called.
});
/**
* Shows a notification when the add-in command is executed.
* @param event
*/
function action(event: Office.AddinCommands.Event) {
const message: Office.NotificationMessageDetails = {
type: Office.MailboxEnums.ItemNotificationMessageType.InformationalMessage,
message: "Performed action.",
icon: "Icon.80x80",
persistent: true,
};
// Show a notification message.
Office.context.mailbox.item?.notificationMessages.replaceAsync(
"ActionPerformanceNotification",
message
);
// Be sure to indicate when the add-in command function is complete.
event.completed();
}
// Register the function with Office.
Office.actions.associate("action", action);

View file

@ -0,0 +1,47 @@
import * as React from "react";
import Header from "./Header";
import HeroList, { HeroListItem } from "./HeroList";
import TextInsertion from "./TextInsertion";
import { makeStyles } from "@fluentui/react-components";
import { Ribbon24Regular, LockOpen24Regular, DesignIdeas24Regular } from "@fluentui/react-icons";
import { insertText } from "../taskpane";
interface AppProps {
title: string;
}
const useStyles = makeStyles({
root: {
minHeight: "100vh",
},
});
const App: React.FC<AppProps> = (props: AppProps) => {
const styles = useStyles();
// The list items are static and won't change at runtime,
// so this should be an ordinary const, not a part of state.
const listItems: HeroListItem[] = [
{
icon: <Ribbon24Regular />,
primaryText: "Achieve more with Office integration",
},
{
icon: <LockOpen24Regular />,
primaryText: "Unlock features and functionality",
},
{
icon: <DesignIdeas24Regular />,
primaryText: "Create and visualize like a pro",
},
];
return (
<div className={styles.root}>
<Header logo="assets/logo-filled.png" title={props.title} message="Welcome" />
<HeroList message="Discover what this add-in can do for you today!" items={listItems} />
<TextInsertion insertText={insertText} />
</div>
);
};
export default App;

View file

@ -0,0 +1,38 @@
import * as React from "react";
import { Image, tokens, makeStyles } from "@fluentui/react-components";
export interface HeaderProps {
title: string;
logo: string;
message: string;
}
const useStyles = makeStyles({
welcome__header: {
display: "flex",
flexDirection: "column",
alignItems: "center",
paddingBottom: "30px",
paddingTop: "100px",
backgroundColor: tokens.colorNeutralBackground3,
},
message: {
fontSize: tokens.fontSizeHero900,
fontWeight: tokens.fontWeightRegular,
fontColor: tokens.colorNeutralBackgroundStatic,
},
});
const Header: React.FC<HeaderProps> = (props: HeaderProps) => {
const { title, logo, message } = props;
const styles = useStyles();
return (
<section className={styles.welcome__header}>
<Image width="90" height="90" src={logo} alt={title} />
<h1 className={styles.message}>{message}</h1>
</section>
);
};
export default Header;

View file

@ -0,0 +1,62 @@
import * as React from "react";
import { tokens, makeStyles } from "@fluentui/react-components";
export interface HeroListItem {
icon: React.JSX.Element;
primaryText: string;
}
export interface HeroListProps {
message: string;
items: HeroListItem[];
}
const useStyles = makeStyles({
list: {
marginTop: "20px",
},
listItem: {
paddingBottom: "20px",
display: "flex",
},
icon: {
marginRight: "10px",
},
itemText: {
fontSize: tokens.fontSizeBase300,
fontColor: tokens.colorNeutralBackgroundStatic,
},
welcome__main: {
width: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center",
},
message: {
fontSize: tokens.fontSizeBase500,
fontColor: tokens.colorNeutralBackgroundStatic,
fontWeight: tokens.fontWeightRegular,
paddingLeft: "10px",
paddingRight: "10px",
},
});
const HeroList: React.FC<HeroListProps> = (props: HeroListProps) => {
const { items, message } = props;
const styles = useStyles();
const listItems = items.map((item, index) => (
<li className={styles.listItem} key={index}>
<i className={styles.icon}>{item.icon}</i>
<span className={styles.itemText}>{item.primaryText}</span>
</li>
));
return (
<div className={styles.welcome__main}>
<h2 className={styles.message}>{message}</h2>
<ul className={styles.list}>{listItems}</ul>
</div>
);
};
export default HeroList;

View file

@ -0,0 +1,57 @@
import * as React from "react";
import { useState } from "react";
import { Button, Field, Textarea, tokens, makeStyles } from "@fluentui/react-components";
/* global HTMLTextAreaElement */
interface TextInsertionProps {
insertText: (text: string) => void;
}
const useStyles = makeStyles({
instructions: {
fontWeight: tokens.fontWeightSemibold,
marginTop: "20px",
marginBottom: "10px",
},
textPromptAndInsertion: {
display: "flex",
flexDirection: "column",
alignItems: "center",
},
textAreaField: {
marginLeft: "20px",
marginTop: "30px",
marginBottom: "20px",
marginRight: "20px",
maxWidth: "50%",
},
});
const TextInsertion: React.FC<TextInsertionProps> = (props: TextInsertionProps) => {
const [text, setText] = useState<string>("Some text.");
const handleTextInsertion = async () => {
await props.insertText(text);
};
const handleTextChange = async (event: React.ChangeEvent<HTMLTextAreaElement>) => {
setText(event.target.value);
};
const styles = useStyles();
return (
<div className={styles.textPromptAndInsertion}>
<Field className={styles.textAreaField} size="large" label="Enter text to be inserted into the document.">
<Textarea size="large" value={text} onChange={handleTextChange} />
</Field>
<Field className={styles.instructions}>Click the button to insert text.</Field>
<Button appearance="primary" disabled={false} size="large" onClick={handleTextInsertion}>
Insert text
</Button>
</div>
);
};
export default TextInsertion;

View file

@ -0,0 +1,27 @@
import * as React from "react";
import { createRoot } from "react-dom/client";
import App from "./components/App";
import { FluentProvider, webLightTheme } from "@fluentui/react-components";
/* global document, Office, module, require, HTMLElement */
const title = "Contoso Task Pane Add-in";
const rootElement: HTMLElement | null = document.getElementById("container");
const root = rootElement ? createRoot(rootElement) : undefined;
/* Render application after Office initializes */
Office.onReady(() => {
root?.render(
<FluentProvider theme={webLightTheme}>
<App title={title} />
</FluentProvider>
);
});
if ((module as any).hot) {
(module as any).hot.accept("./components/App", () => {
const NextApp = require("./components/App").default;
root?.render(NextApp);
});
}

View file

@ -0,0 +1,21 @@
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -->
<!-- See LICENSE in the project root for license information -->
<!doctype html>
<html lang="en" data-framework="typescript">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Contoso Task Pane Add-in</title>
<!-- Office JavaScript API -->
<script type="text/javascript" src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>
</head>
<body style="width: 100%; height: 100%; margin: 0; padding: 0;">
<div id="container"></div>
</body>
</html>

View file

@ -0,0 +1,14 @@
/* global Word console */
export async function insertText(text: string) {
// Write text to the document.
try {
await Word.run(async (context) => {
let body = context.document.body;
body.insertParagraph(text, Word.InsertLocation.end);
await context.sync();
});
} catch (error) {
console.log("Error: " + error);
}
}

29
addin/tsconfig.json Normal file
View file

@ -0,0 +1,29 @@
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"jsx": "react",
"module": "ES2020",
"moduleResolution": "bundler",
"noImplicitReturns": true,
"noUnusedParameters": true,
"outDir": "dist",
"sourceMap": true,
"target": "es6",
"lib": [
"es7",
"dom"
],
},
"exclude": [
"node_modules"
],
"ts-node": {
"compilerOptions": {
"module": "commonjs"
},
"files": true
}
}

111
addin/webpack.config.js Normal file
View file

@ -0,0 +1,111 @@
/* eslint-disable no-undef */
const devCerts = require("office-addin-dev-certs");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack");
const urlDev = "https://localhost:3000/";
const urlProd = "https://www.contoso.com/"; // CHANGE THIS TO YOUR PRODUCTION DEPLOYMENT LOCATION
async function getHttpsOptions() {
const httpsOptions = await devCerts.getHttpsServerOptions();
return { ca: httpsOptions.ca, key: httpsOptions.key, cert: httpsOptions.cert };
}
module.exports = async (env, options) => {
const dev = options.mode === "development";
const config = {
devtool: "source-map",
entry: {
polyfill: ["core-js/stable", "regenerator-runtime/runtime"],
react: ["react", "react-dom"],
taskpane: {
import: ["./src/taskpane/index.tsx", "./src/taskpane/taskpane.html"],
dependOn: "react",
},
commands: "./src/commands/commands.ts",
},
output: {
clean: true,
},
resolve: {
extensions: [".ts", ".tsx", ".html", ".js"],
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: ["ts-loader"],
},
{
test: /\.html$/,
exclude: /node_modules/,
use: "html-loader",
},
{
test: /\.(png|jpg|jpeg|ttf|woff|woff2|gif|ico)$/,
type: "asset/resource",
generator: {
filename: "assets/[name][ext][query]",
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
filename: "taskpane.html",
template: "./src/taskpane/taskpane.html",
chunks: ["polyfill", "taskpane", "react"],
}),
new CopyWebpackPlugin({
patterns: [
{
from: "assets/*",
to: "assets/[name][ext][query]",
},
{
from: "manifest*.xml",
to: "[name]" + "[ext]",
transform(content) {
if (dev) {
return content;
} else {
return content.toString().replace(new RegExp(urlDev, "g"), urlProd);
}
},
},
],
}),
new HtmlWebpackPlugin({
filename: "commands.html",
template: "./src/commands/commands.html",
chunks: ["polyfill", "commands"],
}),
new webpack.ProvidePlugin({
Promise: ["es6-promise", "Promise"],
}),
],
devServer: {
hot: true,
headers: {
"Access-Control-Allow-Origin": "*",
},
server: {
type: "https",
options: env.WEBPACK_BUILD || options.https !== undefined ? options.https : await getHttpsOptions(),
},
port: process.env.npm_package_config_dev_server_port || 3000,
},
};
return config;
};