From 4e1b9ed37d9184798fc41c61015c3dddea2d2db5 Mon Sep 17 00:00:00 2001 From: mathieufournier Date: Fri, 13 Dec 2024 15:31:10 -0500 Subject: [PATCH 01/15] Fixed focus + visual update when accudraw dialog becomes visible, when the mouse move and when typing in the inputs. --- ....timestamp-1734121131273-93739ffd1c176.mjs | 89 +++++++++++++++++ .../accudraw/AccuDrawFieldContainer.tsx | 65 ++++++++----- .../accudraw/AccuDrawInputField.scss | 15 --- .../accudraw/AccuDrawInputField.tsx | 95 ++++++++++--------- .../appui-react/accudraw/FrameworkAccuDraw.ts | 18 ++-- .../appui-react/preview/PreviewFeatures.tsx | 11 +-- ...owBearingLettersInAccuDrawInputFields.tsx} | 8 +- .../useEnableColorlessAccuDrawInputFields.tsx | 13 --- .../test/accudraw/AccuDrawInputField.test.tsx | 39 ++++---- 9 files changed, 213 insertions(+), 140 deletions(-) create mode 100644 apps/test-app/vite.config.ts.timestamp-1734121131273-93739ffd1c176.mjs rename ui/appui-react/src/appui-react/preview/{allow-letters-in-accudraw-input-fields/useAllowLettersInAccuDrawInputFields.tsx => allow-bearing-letters-in-accudraw-input-fields/useAllowBearingLettersInAccuDrawInputFields.tsx} (58%) delete mode 100644 ui/appui-react/src/appui-react/preview/enable-colorless-accudraw-input-fields/useEnableColorlessAccuDrawInputFields.tsx diff --git a/apps/test-app/vite.config.ts.timestamp-1734121131273-93739ffd1c176.mjs b/apps/test-app/vite.config.ts.timestamp-1734121131273-93739ffd1c176.mjs new file mode 100644 index 00000000000..788b733c3cc --- /dev/null +++ b/apps/test-app/vite.config.ts.timestamp-1734121131273-93739ffd1c176.mjs @@ -0,0 +1,89 @@ +// vite.config.ts +import fs from "fs"; +import { createLogger, defineConfig, loadEnv } from "file:///E:/Repos/appui/a/appui/common/temp/node_modules/.pnpm/vite@5.4.7_@types+node@20.17.8_sass@1.80.5/node_modules/vite/dist/node/index.js"; +import react from "file:///E:/Repos/appui/a/appui/common/temp/node_modules/.pnpm/@vitejs+plugin-react-swc@3.7.0_@swc+helpers@0.5.15_vite@5.4.7_@types+node@20.17.8_sass@1.80.5_/node_modules/@vitejs/plugin-react-swc/index.mjs"; +import { viteStaticCopy } from "file:///E:/Repos/appui/a/appui/common/temp/node_modules/.pnpm/vite-plugin-static-copy@1.0.6_vite@5.4.7_@types+node@20.17.8_sass@1.80.5_/node_modules/vite-plugin-static-copy/dist/index.js"; +import { TanStackRouterVite } from "file:///E:/Repos/appui/a/appui/common/temp/node_modules/.pnpm/@tanstack+router-plugin@1.87.7_vite@5.4.7_@types+node@20.17.8_sass@1.80.5_/node_modules/@tanstack/router-plugin/dist/esm/vite.js"; +var customLogger = createLogger(); +var warn = customLogger.warn; +customLogger.warn = (msg, options) => { + if (msg.includes(`Module "fs" has been externalized`) && msg.includes("node_modules/electron/index.js")) + return; + if (msg.includes(`Module "path" has been externalized`) && msg.includes("node_modules/electron/index.js")) + return; + warn(msg, options); +}; +var vite_config_default = defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), ""); + const bimDir = env.IMJS_BIM_DIR; + const files = bimDir ? fs.readdirSync(bimDir) : []; + const bimFiles = files.filter( + (file) => file.endsWith(".bim") || file.endsWith(".ibim") + ); + return { + build: { + chunkSizeWarningLimit: 7e3 + }, + customLogger, + css: { + preprocessorOptions: { + scss: { + api: "modern-compiler" + } + } + }, + plugins: [ + TanStackRouterVite(), + react(), + viteStaticCopy({ + targets: [ + { + // copy assets from `@itwin` dependencies + src: "./node_modules/@itwin/*/lib/public/*", + dest: "." + }, + { + // copy localization files + src: "./lib/locales", + dest: "." + }, + { + src: "./lib/locales/en/**", + dest: "./locales/en-US" + } + ] + }), + { + name: "markdown-loader", + transform(code, id) { + if (!id.endsWith(".md")) return; + return `export default ${JSON.stringify(code)};`; + } + } + ], + resolve: { + alias: [ + { + find: "~@itwin/appui-react", + replacement: "@itwin/appui-react" + }, + { + find: "~@itwin/core-react", + replacement: "@itwin/core-react" + } + ] + }, + envPrefix: "IMJS_", + server: { + port: 3e3, + strictPort: true + }, + define: { + __BIM_FILES__: JSON.stringify(bimFiles) + } + }; +}); +export { + vite_config_default as default +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJFOlxcXFxSZXBvc1xcXFxhcHB1aVxcXFxhXFxcXGFwcHVpXFxcXGFwcHNcXFxcdGVzdC1hcHBcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkU6XFxcXFJlcG9zXFxcXGFwcHVpXFxcXGFcXFxcYXBwdWlcXFxcYXBwc1xcXFx0ZXN0LWFwcFxcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vRTovUmVwb3MvYXBwdWkvYS9hcHB1aS9hcHBzL3Rlc3QtYXBwL3ZpdGUuY29uZmlnLnRzXCI7LyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqIENvcHlyaWdodCAoYykgQmVudGxleSBTeXN0ZW1zLCBJbmNvcnBvcmF0ZWQuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiBTZWUgTElDRU5TRS5tZCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIHRlcm1zIGFuZCBmdWxsIGNvcHlyaWdodCBub3RpY2UuXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmltcG9ydCBmcyBmcm9tIFwiZnNcIjtcbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciwgZGVmaW5lQ29uZmlnLCBsb2FkRW52IH0gZnJvbSBcInZpdGVcIjtcbmltcG9ydCByZWFjdCBmcm9tIFwiQHZpdGVqcy9wbHVnaW4tcmVhY3Qtc3djXCI7XG5pbXBvcnQgeyB2aXRlU3RhdGljQ29weSB9IGZyb20gXCJ2aXRlLXBsdWdpbi1zdGF0aWMtY29weVwiO1xuaW1wb3J0IHsgVGFuU3RhY2tSb3V0ZXJWaXRlIH0gZnJvbSBcIkB0YW5zdGFjay9yb3V0ZXItcGx1Z2luL3ZpdGVcIjtcblxuY29uc3QgY3VzdG9tTG9nZ2VyID0gY3JlYXRlTG9nZ2VyKCk7XG5jb25zdCB3YXJuID0gY3VzdG9tTG9nZ2VyLndhcm47XG5cbmN1c3RvbUxvZ2dlci53YXJuID0gKG1zZywgb3B0aW9ucykgPT4ge1xuICBpZiAoXG4gICAgbXNnLmluY2x1ZGVzKGBNb2R1bGUgXCJmc1wiIGhhcyBiZWVuIGV4dGVybmFsaXplZGApICYmXG4gICAgbXNnLmluY2x1ZGVzKFwibm9kZV9tb2R1bGVzL2VsZWN0cm9uL2luZGV4LmpzXCIpXG4gIClcbiAgICByZXR1cm47XG4gIGlmIChcbiAgICBtc2cuaW5jbHVkZXMoYE1vZHVsZSBcInBhdGhcIiBoYXMgYmVlbiBleHRlcm5hbGl6ZWRgKSAmJlxuICAgIG1zZy5pbmNsdWRlcyhcIm5vZGVfbW9kdWxlcy9lbGVjdHJvbi9pbmRleC5qc1wiKVxuICApXG4gICAgcmV0dXJuO1xuICB3YXJuKG1zZywgb3B0aW9ucyk7XG59O1xuXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKCh7IG1vZGUgfSkgPT4ge1xuICBjb25zdCBlbnYgPSBsb2FkRW52KG1vZGUsIHByb2Nlc3MuY3dkKCksIFwiXCIpO1xuICBjb25zdCBiaW1EaXIgPSBlbnYuSU1KU19CSU1fRElSO1xuICBjb25zdCBmaWxlcyA9IGJpbURpciA/IGZzLnJlYWRkaXJTeW5jKGJpbURpcikgOiBbXTtcbiAgY29uc3QgYmltRmlsZXMgPSBmaWxlcy5maWx0ZXIoXG4gICAgKGZpbGUpID0+IGZpbGUuZW5kc1dpdGgoXCIuYmltXCIpIHx8IGZpbGUuZW5kc1dpdGgoXCIuaWJpbVwiKVxuICApO1xuICByZXR1cm4ge1xuICAgIGJ1aWxkOiB7XG4gICAgICBjaHVua1NpemVXYXJuaW5nTGltaXQ6IDcwMDAsXG4gICAgfSxcbiAgICBjdXN0b21Mb2dnZXIsXG4gICAgY3NzOiB7XG4gICAgICBwcmVwcm9jZXNzb3JPcHRpb25zOiB7XG4gICAgICAgIHNjc3M6IHtcbiAgICAgICAgICBhcGk6IFwibW9kZXJuLWNvbXBpbGVyXCIsXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH0sXG4gICAgcGx1Z2luczogW1xuICAgICAgVGFuU3RhY2tSb3V0ZXJWaXRlKCksXG4gICAgICByZWFjdCgpLFxuICAgICAgdml0ZVN0YXRpY0NvcHkoe1xuICAgICAgICB0YXJnZXRzOiBbXG4gICAgICAgICAge1xuICAgICAgICAgICAgLy8gY29weSBhc3NldHMgZnJvbSBgQGl0d2luYCBkZXBlbmRlbmNpZXNcbiAgICAgICAgICAgIHNyYzogXCIuL25vZGVfbW9kdWxlcy9AaXR3aW4vKi9saWIvcHVibGljLypcIixcbiAgICAgICAgICAgIGRlc3Q6IFwiLlwiLFxuICAgICAgICAgIH0sXG4gICAgICAgICAge1xuICAgICAgICAgICAgLy8gY29weSBsb2NhbGl6YXRpb24gZmlsZXNcbiAgICAgICAgICAgIHNyYzogXCIuL2xpYi9sb2NhbGVzXCIsXG4gICAgICAgICAgICBkZXN0OiBcIi5cIixcbiAgICAgICAgICB9LFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIHNyYzogXCIuL2xpYi9sb2NhbGVzL2VuLyoqXCIsXG4gICAgICAgICAgICBkZXN0OiBcIi4vbG9jYWxlcy9lbi1VU1wiLFxuICAgICAgICAgIH0sXG4gICAgICAgIF0sXG4gICAgICB9KSxcbiAgICAgIHtcbiAgICAgICAgbmFtZTogXCJtYXJrZG93bi1sb2FkZXJcIixcbiAgICAgICAgdHJhbnNmb3JtKGNvZGUsIGlkKSB7XG4gICAgICAgICAgaWYgKCFpZC5lbmRzV2l0aChcIi5tZFwiKSkgcmV0dXJuO1xuXG4gICAgICAgICAgLy8gRm9yIC5tZCBmaWxlcywgZ2V0IHRoZSByYXcgY29udGVudFxuICAgICAgICAgIHJldHVybiBgZXhwb3J0IGRlZmF1bHQgJHtKU09OLnN0cmluZ2lmeShjb2RlKX07YDtcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgXSxcbiAgICByZXNvbHZlOiB7XG4gICAgICBhbGlhczogW1xuICAgICAgICB7XG4gICAgICAgICAgZmluZDogXCJ+QGl0d2luL2FwcHVpLXJlYWN0XCIsXG4gICAgICAgICAgcmVwbGFjZW1lbnQ6IFwiQGl0d2luL2FwcHVpLXJlYWN0XCIsXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBmaW5kOiBcIn5AaXR3aW4vY29yZS1yZWFjdFwiLFxuICAgICAgICAgIHJlcGxhY2VtZW50OiBcIkBpdHdpbi9jb3JlLXJlYWN0XCIsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH0sXG4gICAgZW52UHJlZml4OiBcIklNSlNfXCIsXG4gICAgc2VydmVyOiB7XG4gICAgICBwb3J0OiAzMDAwLFxuICAgICAgc3RyaWN0UG9ydDogdHJ1ZSxcbiAgICB9LFxuICAgIGRlZmluZToge1xuICAgICAgX19CSU1fRklMRVNfXzogSlNPTi5zdHJpbmdpZnkoYmltRmlsZXMpLFxuICAgIH0sXG4gIH07XG59KTtcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFJQSxPQUFPLFFBQVE7QUFDZixTQUFTLGNBQWMsY0FBYyxlQUFlO0FBQ3BELE9BQU8sV0FBVztBQUNsQixTQUFTLHNCQUFzQjtBQUMvQixTQUFTLDBCQUEwQjtBQUVuQyxJQUFNLGVBQWUsYUFBYTtBQUNsQyxJQUFNLE9BQU8sYUFBYTtBQUUxQixhQUFhLE9BQU8sQ0FBQyxLQUFLLFlBQVk7QUFDcEMsTUFDRSxJQUFJLFNBQVMsbUNBQW1DLEtBQ2hELElBQUksU0FBUyxnQ0FBZ0M7QUFFN0M7QUFDRixNQUNFLElBQUksU0FBUyxxQ0FBcUMsS0FDbEQsSUFBSSxTQUFTLGdDQUFnQztBQUU3QztBQUNGLE9BQUssS0FBSyxPQUFPO0FBQ25CO0FBR0EsSUFBTyxzQkFBUSxhQUFhLENBQUMsRUFBRSxLQUFLLE1BQU07QUFDeEMsUUFBTSxNQUFNLFFBQVEsTUFBTSxRQUFRLElBQUksR0FBRyxFQUFFO0FBQzNDLFFBQU0sU0FBUyxJQUFJO0FBQ25CLFFBQU0sUUFBUSxTQUFTLEdBQUcsWUFBWSxNQUFNLElBQUksQ0FBQztBQUNqRCxRQUFNLFdBQVcsTUFBTTtBQUFBLElBQ3JCLENBQUMsU0FBUyxLQUFLLFNBQVMsTUFBTSxLQUFLLEtBQUssU0FBUyxPQUFPO0FBQUEsRUFDMUQ7QUFDQSxTQUFPO0FBQUEsSUFDTCxPQUFPO0FBQUEsTUFDTCx1QkFBdUI7QUFBQSxJQUN6QjtBQUFBLElBQ0E7QUFBQSxJQUNBLEtBQUs7QUFBQSxNQUNILHFCQUFxQjtBQUFBLFFBQ25CLE1BQU07QUFBQSxVQUNKLEtBQUs7QUFBQSxRQUNQO0FBQUEsTUFDRjtBQUFBLElBQ0Y7QUFBQSxJQUNBLFNBQVM7QUFBQSxNQUNQLG1CQUFtQjtBQUFBLE1BQ25CLE1BQU07QUFBQSxNQUNOLGVBQWU7QUFBQSxRQUNiLFNBQVM7QUFBQSxVQUNQO0FBQUE7QUFBQSxZQUVFLEtBQUs7QUFBQSxZQUNMLE1BQU07QUFBQSxVQUNSO0FBQUEsVUFDQTtBQUFBO0FBQUEsWUFFRSxLQUFLO0FBQUEsWUFDTCxNQUFNO0FBQUEsVUFDUjtBQUFBLFVBQ0E7QUFBQSxZQUNFLEtBQUs7QUFBQSxZQUNMLE1BQU07QUFBQSxVQUNSO0FBQUEsUUFDRjtBQUFBLE1BQ0YsQ0FBQztBQUFBLE1BQ0Q7QUFBQSxRQUNFLE1BQU07QUFBQSxRQUNOLFVBQVUsTUFBTSxJQUFJO0FBQ2xCLGNBQUksQ0FBQyxHQUFHLFNBQVMsS0FBSyxFQUFHO0FBR3pCLGlCQUFPLGtCQUFrQixLQUFLLFVBQVUsSUFBSSxDQUFDO0FBQUEsUUFDL0M7QUFBQSxNQUNGO0FBQUEsSUFDRjtBQUFBLElBQ0EsU0FBUztBQUFBLE1BQ1AsT0FBTztBQUFBLFFBQ0w7QUFBQSxVQUNFLE1BQU07QUFBQSxVQUNOLGFBQWE7QUFBQSxRQUNmO0FBQUEsUUFDQTtBQUFBLFVBQ0UsTUFBTTtBQUFBLFVBQ04sYUFBYTtBQUFBLFFBQ2Y7QUFBQSxNQUNGO0FBQUEsSUFDRjtBQUFBLElBQ0EsV0FBVztBQUFBLElBQ1gsUUFBUTtBQUFBLE1BQ04sTUFBTTtBQUFBLE1BQ04sWUFBWTtBQUFBLElBQ2Q7QUFBQSxJQUNBLFFBQVE7QUFBQSxNQUNOLGVBQWUsS0FBSyxVQUFVLFFBQVE7QUFBQSxJQUN4QztBQUFBLEVBQ0Y7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo= diff --git a/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx b/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx index 0250f447694..5ecbdbf1097 100644 --- a/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx +++ b/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx @@ -22,7 +22,6 @@ import angleIconSvg from "./angle.svg"; import distanceIconSvg from "./distance.svg"; import { UiFramework } from "../UiFramework.js"; import type { UiStateStorage } from "../uistate/UiStateStorage.js"; -import { useEnableColorlessAccuDrawInputFields } from "../preview/enable-colorless-accudraw-input-fields/useEnableColorlessAccuDrawInputFields.js"; /** Properties for [[AccuDrawFieldContainer]] component * @public @@ -36,6 +35,8 @@ export interface AccuDrawFieldContainerProps extends CommonProps { uiSettingsStorage?: UiStateStorage; /** @internal */ showZOverride?: boolean; + /** Indicates whether the field is displaying a bearing angles. */ + isBearingAngle?: boolean; } let AccuDrawContainerIndex = 0; @@ -60,6 +61,7 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { orientation, uiSettingsStorage, showZOverride, + isBearingAngle = false, ...otherProps } = props; @@ -71,8 +73,6 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { const distanceInputRef = React.useRef(null); const focusField = React.useRef(undefined); const [mode, setMode] = React.useState(() => IModelApp.accuDraw.compassMode); - const enableColorlessAccuDrawInputFields = - useEnableColorlessAccuDrawInputFields(); const [xLock, setXLock] = React.useState(() => IModelApp.accuDraw.getFieldLock(ItemField.X_Item) ); @@ -154,6 +154,13 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { [getInputToFocus] ); + React.useEffect(()=>{ + // Set the focus to the first input field when the component is mounted and when the compass mode changes. + const itemToFocus = mode === CompassMode.Polar ? ItemField.DIST_Item : ItemField.X_Item; + IModelApp.accuDraw.setFocusItem(itemToFocus); + setFocusToField(itemToFocus); + },[mode, setFocusToField, getInputToFocus]); + React.useEffect(() => { return FrameworkAccuDraw.onAccuDrawSetFieldLockEvent.addListener((args) => { switch (args.field) { @@ -206,7 +213,30 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { [] ); + const focusNextField = React.useCallback((field: ItemField) => { + switch (field) { + case ItemField.X_Item: + IModelApp.accuDraw.setFocusItem(ItemField.Y_Item); + break; + case ItemField.Y_Item: + showZ ? + IModelApp.accuDraw.setFocusItem(ItemField.Z_Item) : + IModelApp.accuDraw.setFocusItem(ItemField.X_Item); + break; + case ItemField.Z_Item: + IModelApp.accuDraw.setFocusItem(ItemField.X_Item); + break; + case ItemField.ANGLE_Item: + IModelApp.accuDraw.setFocusItem(ItemField.DIST_Item); + break; + case ItemField.DIST_Item: + IModelApp.accuDraw.setFocusItem(ItemField.ANGLE_Item); + break; + } + }, [showZ]); + const handleEscPressed = React.useCallback(() => { + IModelApp.accuDraw.unlockAllFields(); UiFramework.keyboardShortcuts.setFocusToHome(); }, []); @@ -301,7 +331,6 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { className ); - const delay = 250; const labelCentered = xLabel !== undefined && xLabel.length === 1 && @@ -317,11 +346,6 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { handleValueChanged(ItemField.X_Item, stringValue) } onEscPressed={handleEscPressed} + onTabPressed={focusNextField} /> handleValueChanged(ItemField.Y_Item, stringValue) } onEscPressed={handleEscPressed} + onTabPressed={focusNextField} /> {showZ && ( handleValueChanged(ItemField.Z_Item, stringValue) } onEscPressed={handleEscPressed} + onTabPressed={focusNextField} /> )} @@ -392,7 +406,6 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { handleValueChanged(ItemField.DIST_Item, stringValue) } onEscPressed={handleEscPressed} + onTabPressed={focusNextField} /> handleValueChanged(ItemField.ANGLE_Item, stringValue) } onEscPressed={handleEscPressed} + onTabPressed={focusNextField} /> )} diff --git a/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.scss b/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.scss index 42d683ab418..663fee58eae 100644 --- a/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.scss +++ b/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.scss @@ -39,18 +39,3 @@ width: 22px; @include uicore-centered; } - -.uifw-accudraw-x-value { - background-color: var(--iui-color-background-negative-hover); - color: var(--iui-color-white); -} - -.uifw-accudraw-y-value { - background-color: var(--iui-color-background-positive-hover); - color: var(--iui-color-white); -} - -.uifw-accudraw-z-value { - background-color: var(--iui-color-background-accent-hover); - color: var(--iui-color-white); -} diff --git a/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx b/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx index 9a26acda79b..6f467e16661 100644 --- a/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx +++ b/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx @@ -18,7 +18,7 @@ import { Input } from "@itwin/itwinui-react"; import { FrameworkAccuDraw } from "./FrameworkAccuDraw.js"; import { UiFramework } from "../UiFramework.js"; import { SvgLock } from "@itwin/itwinui-icons-react"; -import { useAllowLettersInAccuDrawInputFields } from "../preview/allow-letters-in-accudraw-input-fields/useAllowLettersInAccuDrawInputFields.js"; +import { useAllowBearingLettersInAccuDrawInputFields } from "../preview/allow-bearing-letters-in-accudraw-input-fields/useAllowBearingLettersInAccuDrawInputFields.js"; function isLetter(char: string): boolean { return char.length === 1 && char.toLowerCase() !== char.toUpperCase(); @@ -36,6 +36,8 @@ export interface AccuDrawInputFieldProps extends CommonProps { id: string; /** Indicates whether field is locked */ isLocked?: boolean; + /** Indicates whether the field is displaying bearing angles. */ + isBearingAngle?: boolean; /** label for the input element */ label?: string; /** Icon for the input element. @@ -53,12 +55,12 @@ export interface AccuDrawInputFieldProps extends CommonProps { labelCentered?: boolean; /** Triggered when the content is changed */ onValueChanged: (stringValue: string) => void; - /** Frequency to poll for changes in value, in milliseconds */ - valueChangedDelay?: number; /** Listens for keypress */ onEnterPressed?: () => void; /** Listens for keypress */ onEscPressed?: () => void; + /** Listens for keypress */ + onTabPressed?: (field: ItemField) => void; /** Provides ability to return reference to HTMLInputElement */ ref?: React.Ref; } @@ -82,22 +84,20 @@ const ForwardRefAccuDrawInput = React.forwardRef< labelCentered, field, isLocked, + isBearingAngle = false, onValueChanged, - valueChangedDelay, onEnterPressed, onEscPressed, + onTabPressed, ...inputProps } = props; const [stringValue, setStringValue] = React.useState(""); - const timeoutId = React.useRef(0); - const [needValueChanged, setNeedValueChanged] = React.useState(false); const [needSelection, setNeedSelection] = React.useState(false); const [isFocusField, setIsFocusField] = React.useState(false); const inputElementRef = React.useRef(null); const refs = useRefs(inputElementRef, ref); // combine ref needed for target with the forwardRef needed by the Parent when parent is a Type Editor. - const allowLettersInAccuDrawInputFields = - useAllowLettersInAccuDrawInputFields(); + const allowBearingLettersInAccuDrawInputFields = useAllowBearingLettersInAccuDrawInputFields(); React.useEffect(() => { const formattedValue = FrameworkAccuDraw.getFieldDisplayValue(field); @@ -106,69 +106,70 @@ const ForwardRefAccuDrawInput = React.forwardRef< const handleChange = React.useCallback( (event: React.ChangeEvent): void => { - const value = event.currentTarget.value; + let value = event.currentTarget.value; if (value === undefined) return; if (stringValue !== value) { + if(isBearingAngle){ + // Parsing bearing only works properly when parsing UpperCase letters. Ex : S45°00'00"E <- ok, s45°00'00"e <- doesnt work. + value = value.toUpperCase(); + if(value.length > stringValue.length){ + // Automatically add Bearing special characters to help users type the angle. Ex: S45°00'00"E (degrees `°`, minutes `'`, seconds `"`) + if(value.length === 3 && value[2] !== "°"){ + value += "°"; + } + else if(value.length === 6 && value[5] !== "'"){ + value += "'"; + } + else if(value.length === 9 && value[8] !== "\""){ + value += "\""; + } + } + } setStringValue(value); - setNeedValueChanged(true); + onValueChanged(value); } }, - [stringValue] + [stringValue, isBearingAngle, onValueChanged] ); - const unsetTimeout = (): void => { - if (timeoutId) { - window.clearTimeout(timeoutId.current); - timeoutId.current = 0; - } - }; - - React.useEffect(() => { - // After setStringValue & re-render - if (needValueChanged) { - if (valueChangedDelay) { - unsetTimeout(); - timeoutId.current = window.setTimeout(() => { - onValueChanged(stringValue); - }, valueChangedDelay); - } else { - onValueChanged(stringValue); - } - setNeedValueChanged(false); - } - }, [onValueChanged, valueChangedDelay, stringValue, needValueChanged]); - - React.useEffect(() => { - // On unmount - return () => unsetTimeout(); - }, []); - const handleKeyDown = React.useCallback( (e: React.KeyboardEvent) => { switch (e.key) { case Key.Escape.valueOf(): onEscPressed && onEscPressed(); + // Prevent ToolAdmin from receiving the event when Escape is pressed in the input field. + // If Escape is pressed again, the input should not be focused anymore, so this code will not be called and ToolAdmin will receive the event normally. + e.stopPropagation(); return; case Key.Enter.valueOf(): onEnterPressed && onEnterPressed(); return; + case Key.Tab.valueOf(): + onTabPressed && onTabPressed(field); + // Prevent from focusing other elements in the page. Act like a focus trap. + e.preventDefault(); + return; } if (isLetter(e.key)) { - if (!allowLettersInAccuDrawInputFields) { - e.preventDefault(); - UiFramework.keyboardShortcuts.processKey( - e.key, - e.altKey, - e.ctrlKey, - e.shiftKey - ); + if(isBearingAngle && allowBearingLettersInAccuDrawInputFields){ + const bearingReservedKeys = ['n', 's', 'e', 'w', 'N', 'S', 'E', 'W']; + if(bearingReservedKeys.includes(e.key)){ + return; // The letter is displayed in the Bearing input field. + } } + e.preventDefault(); + UiFramework.keyboardShortcuts.processKey( + e.key, + e.altKey, + e.ctrlKey, + e.shiftKey + ); } }, - [onEscPressed, onEnterPressed, allowLettersInAccuDrawInputFields] + [onEscPressed, onEnterPressed, onTabPressed, isBearingAngle, allowBearingLettersInAccuDrawInputFields, field] ); React.useEffect(() => { diff --git a/ui/appui-react/src/appui-react/accudraw/FrameworkAccuDraw.ts b/ui/appui-react/src/appui-react/accudraw/FrameworkAccuDraw.ts index e06f4d189d6..634d116a210 100644 --- a/ui/appui-react/src/appui-react/accudraw/FrameworkAccuDraw.ts +++ b/ui/appui-react/src/appui-react/accudraw/FrameworkAccuDraw.ts @@ -13,6 +13,7 @@ import { CompassMode, IModelApp, ItemField, + KeyinStatus, NotifyMessageDetails, OutputMessagePriority, QuantityType, @@ -307,11 +308,12 @@ export class FrameworkAccuDraw } private fieldValuesChanged(): void { - this.onFieldValueChange(ItemField.X_Item); - this.onFieldValueChange(ItemField.Y_Item); - this.onFieldValueChange(ItemField.Z_Item); - this.onFieldValueChange(ItemField.ANGLE_Item); - this.onFieldValueChange(ItemField.DIST_Item); + // Only change the value when in Dynamic mode. Other mode are "DontUpdate" when the input is locked and "Partial" when the user is typing. + (this.getKeyinStatus(ItemField.X_Item) === KeyinStatus.Dynamic) && this.onFieldValueChange(ItemField.X_Item); + (this.getKeyinStatus(ItemField.Y_Item) === KeyinStatus.Dynamic) && this.onFieldValueChange(ItemField.Y_Item); + (this.getKeyinStatus(ItemField.Z_Item) === KeyinStatus.Dynamic) && this.onFieldValueChange(ItemField.Z_Item); + (this.getKeyinStatus(ItemField.ANGLE_Item) === KeyinStatus.Dynamic) && this.onFieldValueChange(ItemField.ANGLE_Item); + (this.getKeyinStatus(ItemField.DIST_Item) === KeyinStatus.Dynamic) && this.onFieldValueChange(ItemField.DIST_Item); } public override setFocusItem(index: ItemField): void { @@ -328,7 +330,11 @@ export class FrameworkAccuDraw this.fieldValuesChanged(); - if (!this.dontMoveFocus) this.setFocusItem(this.newFocus); + if (!this.dontMoveFocus && this.compassMode === CompassMode.Rectangular) { + // Changes the focus between X and Y axis depending on the cursor location, in rectangular mode. + // Example : this.newfocus is Y when the cursor is closer to the Y axis. + this.setFocusItem(this.newFocus); + } } /** Determine if the AccuDraw UI has focus. */ diff --git a/ui/appui-react/src/appui-react/preview/PreviewFeatures.tsx b/ui/appui-react/src/appui-react/preview/PreviewFeatures.tsx index bc81a15c4d5..41c86fba1a3 100644 --- a/ui/appui-react/src/appui-react/preview/PreviewFeatures.tsx +++ b/ui/appui-react/src/appui-react/preview/PreviewFeatures.tsx @@ -42,13 +42,11 @@ interface KnownPreviewFeatures { * Discuss or upvote this feature: https://github.com/iTwin/appui/discussions/723 */ widgetActionDropdown: { threshold: number }; - /** If `true`, the accudraw input field will accept letters. Defaults to `false`. + /** If `true`, the accudraw input field will accept some letters for bearing angle. Defaults to `false`. * - * This allows to type in units like meters, feet, inches, bearings and more. + * This allows to type letters for Bearing angles in the accudraw input field. (N, S, E, W) */ - allowLettersInAccuDrawInputFields: boolean; - /** If `true`, the accudraw input field will not have colors for the X, Y, Z axis. Defaults to `false`.*/ - enableColorlessAccuDrawInputFields: boolean; + allowBearingLettersInAccuDrawInputFields: boolean; /** If `true`, popout widgets will not be rendered in a separate element tree, instead widget content will be re-parented to a popout content container. * Alternatively, an array of widget ids can be specified to only re-parent specific widgets. * @note Use {@link useTransientState} to save and restore DOM transient state when re-parenting widgets. @@ -75,8 +73,7 @@ const knownFeaturesObject: Record = { enableMaximizedPanelWidget: undefined, horizontalPanelAlignment: undefined, widgetActionDropdown: undefined, - allowLettersInAccuDrawInputFields: undefined, - enableColorlessAccuDrawInputFields: undefined, + allowBearingLettersInAccuDrawInputFields: undefined, reparentPopoutWidgets: undefined, controlWidgetVisibility: undefined, }; diff --git a/ui/appui-react/src/appui-react/preview/allow-letters-in-accudraw-input-fields/useAllowLettersInAccuDrawInputFields.tsx b/ui/appui-react/src/appui-react/preview/allow-bearing-letters-in-accudraw-input-fields/useAllowBearingLettersInAccuDrawInputFields.tsx similarity index 58% rename from ui/appui-react/src/appui-react/preview/allow-letters-in-accudraw-input-fields/useAllowLettersInAccuDrawInputFields.tsx rename to ui/appui-react/src/appui-react/preview/allow-bearing-letters-in-accudraw-input-fields/useAllowBearingLettersInAccuDrawInputFields.tsx index 692595f9817..73991728aa8 100644 --- a/ui/appui-react/src/appui-react/preview/allow-letters-in-accudraw-input-fields/useAllowLettersInAccuDrawInputFields.tsx +++ b/ui/appui-react/src/appui-react/preview/allow-bearing-letters-in-accudraw-input-fields/useAllowBearingLettersInAccuDrawInputFields.tsx @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { usePreviewFeatures } from "../PreviewFeatures.js"; -/** If `true`, the accudraw input field will accept letters. Defaults to `false`. +/** If `true`, the accudraw input field will accept letters for bearing angles `N, S, E, W`. Defaults to `false`. * @internal */ -export function useAllowLettersInAccuDrawInputFields() { - const { allowLettersInAccuDrawInputFields } = usePreviewFeatures(); - return allowLettersInAccuDrawInputFields; +export function useAllowBearingLettersInAccuDrawInputFields() { + const { allowBearingLettersInAccuDrawInputFields } = usePreviewFeatures(); + return allowBearingLettersInAccuDrawInputFields; } diff --git a/ui/appui-react/src/appui-react/preview/enable-colorless-accudraw-input-fields/useEnableColorlessAccuDrawInputFields.tsx b/ui/appui-react/src/appui-react/preview/enable-colorless-accudraw-input-fields/useEnableColorlessAccuDrawInputFields.tsx deleted file mode 100644 index 85ca825b47d..00000000000 --- a/ui/appui-react/src/appui-react/preview/enable-colorless-accudraw-input-fields/useEnableColorlessAccuDrawInputFields.tsx +++ /dev/null @@ -1,13 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Bentley Systems, Incorporated. All rights reserved. - * See LICENSE.md in the project root for license terms and full copyright notice. - *--------------------------------------------------------------------------------------------*/ -import { usePreviewFeatures } from "../PreviewFeatures.js"; - -/** If `true`, the accudraw input field will not have colors for the X, Y, Z axis. Defaults to `false`. - * @internal - */ -export function useEnableColorlessAccuDrawInputFields() { - const { enableColorlessAccuDrawInputFields } = usePreviewFeatures(); - return enableColorlessAccuDrawInputFields; -} diff --git a/ui/appui-react/src/test/accudraw/AccuDrawInputField.test.tsx b/ui/appui-react/src/test/accudraw/AccuDrawInputField.test.tsx index 94826f4b77b..4178f42bd35 100644 --- a/ui/appui-react/src/test/accudraw/AccuDrawInputField.test.tsx +++ b/ui/appui-react/src/test/accudraw/AccuDrawInputField.test.tsx @@ -52,35 +52,30 @@ describe("AccuDrawInputField", () => { expect(spy).toHaveBeenCalledOnce(); }); - it("should call onValueChanged on change after delay", async () => { - vi.useFakeTimers(); - const spy = vi.fn(); + it("should call onEscPressed on ESC", () => { + const spyEsc = vi.fn(); + const spyChanged = vi.fn(); const wrapper = render( ); const input = wrapper.container.querySelector("input"); expect(input).toBeTruthy(); - fireEvent.change(input!, { target: { value: "22.3" } }); - expect((input as HTMLInputElement).value).toEqual("22.3"); - fireEvent.keyDown(input!, { key: Key.Enter }); - expect(spy).not.toBeCalled(); - - vi.advanceTimersByTime(20); - expect(spy).toHaveBeenCalledOnce(); + fireEvent.keyDown(input!, { key: Key.Escape }); + expect(spyEsc).toHaveBeenCalledOnce(); }); - it("should call onEscPressed on ESC", () => { - const spyEsc = vi.fn(); + it("should call onEnterPressed on Enter", () => { + const spyEnter = vi.fn(); const spyChanged = vi.fn(); const wrapper = render( { ); const input = wrapper.container.querySelector("input"); expect(input).toBeTruthy(); - fireEvent.keyDown(input!, { key: Key.Escape }); - expect(spyEsc).toHaveBeenCalledOnce(); + fireEvent.keyDown(input!, { key: Key.Enter }); + expect(spyEnter).toHaveBeenCalledOnce(); }); - it("should call onEnterPressed on Enter", () => { - const spyEnter = vi.fn(); + it("should call onTabPressed on Tab", () => { + const spyTab = vi.fn(); const spyChanged = vi.fn(); const wrapper = render( { ); const input = wrapper.container.querySelector("input"); expect(input).toBeTruthy(); - fireEvent.keyDown(input!, { key: Key.Enter }); - expect(spyEnter).toHaveBeenCalledOnce(); + fireEvent.keyDown(input!, { key: Key.Tab }); + expect(spyTab).toHaveBeenCalledOnce(); }); it("should call UiFramework.keyboardShortcuts.processKey on a letter", () => { From 49a95a0a7ed4c38ac41fc2ff0bd5192d559252eb Mon Sep 17 00:00:00 2001 From: mathieufournier Date: Fri, 13 Dec 2024 15:55:29 -0500 Subject: [PATCH 02/15] Added changes message for accudraw update fixes. --- common/api/appui-react.api.md | 6 ++++-- .../accudraw_update_fixes_2024-12-13-20-55.json | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 common/changes/@itwin/appui-react/accudraw_update_fixes_2024-12-13-20-55.json diff --git a/common/api/appui-react.api.md b/common/api/appui-react.api.md index e8176324cef..561b2541224 100644 --- a/common/api/appui-react.api.md +++ b/common/api/appui-react.api.md @@ -186,6 +186,7 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps): Reac // @public @deprecated export interface AccuDrawFieldContainerProps extends CommonProps { + isBearingAngle?: boolean; orientation: Orientation; // @internal (undocumented) showZOverride?: boolean; @@ -206,6 +207,7 @@ export interface AccuDrawInputFieldProps extends CommonProps { // @deprecated iconSpec?: IconSpec; id: string; + isBearingAngle?: boolean; isLocked?: boolean; label?: string; labelCentered?: boolean; @@ -213,9 +215,9 @@ export interface AccuDrawInputFieldProps extends CommonProps { labelStyle?: React_2.CSSProperties; onEnterPressed?: () => void; onEscPressed?: () => void; + onTabPressed?: (field: ItemField) => void; onValueChanged: (stringValue: string) => void; ref?: React_2.Ref; - valueChangedDelay?: number; } // @public @@ -5345,7 +5347,7 @@ export function useUiStateStorageHandler(): UiStateStorage; // @alpha export function useWidget(): { state: WidgetState; - widgetLocation: "docked" | "popout" | "floating"; + widgetLocation: "popout" | "floating" | "docked"; setState: (widgetState: Omit) => void; }; diff --git a/common/changes/@itwin/appui-react/accudraw_update_fixes_2024-12-13-20-55.json b/common/changes/@itwin/appui-react/accudraw_update_fixes_2024-12-13-20-55.json new file mode 100644 index 00000000000..bbc1d7fa693 --- /dev/null +++ b/common/changes/@itwin/appui-react/accudraw_update_fixes_2024-12-13-20-55.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/appui-react", + "comment": "Accudraw input fields are now focused when the accudraw input field container becomes visible and when the compass mode changes. Fix : The focus does not change between X or Y when in polar mode. Now only updating input fields value when their state is dynamic. Made rectangular inputs color standard to iTwinUI. We can enter letters in the Bearing angle input field. Bearing angle input field automatically adds special characters to facilitate entering bearing angles. Removed the delay after typing in the input field, the visual update should be immediate. The Focus is now trapped in Accudraw input fields, when doing Tab. To focus out of these fields, the user must press Escape. Escape now unlocks all fields. ", + "type": "none" + } + ], + "packageName": "@itwin/appui-react" +} \ No newline at end of file From 80896a853bc5eea8ee3ad4365b49b0e7bd42603f Mon Sep 17 00:00:00 2001 From: mathieufournier Date: Fri, 13 Dec 2024 15:56:44 -0500 Subject: [PATCH 03/15] removed vite config timestamp test file. --- ....timestamp-1734121131273-93739ffd1c176.mjs | 89 ------------------- 1 file changed, 89 deletions(-) delete mode 100644 apps/test-app/vite.config.ts.timestamp-1734121131273-93739ffd1c176.mjs diff --git a/apps/test-app/vite.config.ts.timestamp-1734121131273-93739ffd1c176.mjs b/apps/test-app/vite.config.ts.timestamp-1734121131273-93739ffd1c176.mjs deleted file mode 100644 index 788b733c3cc..00000000000 --- a/apps/test-app/vite.config.ts.timestamp-1734121131273-93739ffd1c176.mjs +++ /dev/null @@ -1,89 +0,0 @@ -// vite.config.ts -import fs from "fs"; -import { createLogger, defineConfig, loadEnv } from "file:///E:/Repos/appui/a/appui/common/temp/node_modules/.pnpm/vite@5.4.7_@types+node@20.17.8_sass@1.80.5/node_modules/vite/dist/node/index.js"; -import react from "file:///E:/Repos/appui/a/appui/common/temp/node_modules/.pnpm/@vitejs+plugin-react-swc@3.7.0_@swc+helpers@0.5.15_vite@5.4.7_@types+node@20.17.8_sass@1.80.5_/node_modules/@vitejs/plugin-react-swc/index.mjs"; -import { viteStaticCopy } from "file:///E:/Repos/appui/a/appui/common/temp/node_modules/.pnpm/vite-plugin-static-copy@1.0.6_vite@5.4.7_@types+node@20.17.8_sass@1.80.5_/node_modules/vite-plugin-static-copy/dist/index.js"; -import { TanStackRouterVite } from "file:///E:/Repos/appui/a/appui/common/temp/node_modules/.pnpm/@tanstack+router-plugin@1.87.7_vite@5.4.7_@types+node@20.17.8_sass@1.80.5_/node_modules/@tanstack/router-plugin/dist/esm/vite.js"; -var customLogger = createLogger(); -var warn = customLogger.warn; -customLogger.warn = (msg, options) => { - if (msg.includes(`Module "fs" has been externalized`) && msg.includes("node_modules/electron/index.js")) - return; - if (msg.includes(`Module "path" has been externalized`) && msg.includes("node_modules/electron/index.js")) - return; - warn(msg, options); -}; -var vite_config_default = defineConfig(({ mode }) => { - const env = loadEnv(mode, process.cwd(), ""); - const bimDir = env.IMJS_BIM_DIR; - const files = bimDir ? fs.readdirSync(bimDir) : []; - const bimFiles = files.filter( - (file) => file.endsWith(".bim") || file.endsWith(".ibim") - ); - return { - build: { - chunkSizeWarningLimit: 7e3 - }, - customLogger, - css: { - preprocessorOptions: { - scss: { - api: "modern-compiler" - } - } - }, - plugins: [ - TanStackRouterVite(), - react(), - viteStaticCopy({ - targets: [ - { - // copy assets from `@itwin` dependencies - src: "./node_modules/@itwin/*/lib/public/*", - dest: "." - }, - { - // copy localization files - src: "./lib/locales", - dest: "." - }, - { - src: "./lib/locales/en/**", - dest: "./locales/en-US" - } - ] - }), - { - name: "markdown-loader", - transform(code, id) { - if (!id.endsWith(".md")) return; - return `export default ${JSON.stringify(code)};`; - } - } - ], - resolve: { - alias: [ - { - find: "~@itwin/appui-react", - replacement: "@itwin/appui-react" - }, - { - find: "~@itwin/core-react", - replacement: "@itwin/core-react" - } - ] - }, - envPrefix: "IMJS_", - server: { - port: 3e3, - strictPort: true - }, - define: { - __BIM_FILES__: JSON.stringify(bimFiles) - } - }; -}); -export { - vite_config_default as default -}; -//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJFOlxcXFxSZXBvc1xcXFxhcHB1aVxcXFxhXFxcXGFwcHVpXFxcXGFwcHNcXFxcdGVzdC1hcHBcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkU6XFxcXFJlcG9zXFxcXGFwcHVpXFxcXGFcXFxcYXBwdWlcXFxcYXBwc1xcXFx0ZXN0LWFwcFxcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vRTovUmVwb3MvYXBwdWkvYS9hcHB1aS9hcHBzL3Rlc3QtYXBwL3ZpdGUuY29uZmlnLnRzXCI7LyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqIENvcHlyaWdodCAoYykgQmVudGxleSBTeXN0ZW1zLCBJbmNvcnBvcmF0ZWQuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiBTZWUgTElDRU5TRS5tZCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIHRlcm1zIGFuZCBmdWxsIGNvcHlyaWdodCBub3RpY2UuXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmltcG9ydCBmcyBmcm9tIFwiZnNcIjtcbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciwgZGVmaW5lQ29uZmlnLCBsb2FkRW52IH0gZnJvbSBcInZpdGVcIjtcbmltcG9ydCByZWFjdCBmcm9tIFwiQHZpdGVqcy9wbHVnaW4tcmVhY3Qtc3djXCI7XG5pbXBvcnQgeyB2aXRlU3RhdGljQ29weSB9IGZyb20gXCJ2aXRlLXBsdWdpbi1zdGF0aWMtY29weVwiO1xuaW1wb3J0IHsgVGFuU3RhY2tSb3V0ZXJWaXRlIH0gZnJvbSBcIkB0YW5zdGFjay9yb3V0ZXItcGx1Z2luL3ZpdGVcIjtcblxuY29uc3QgY3VzdG9tTG9nZ2VyID0gY3JlYXRlTG9nZ2VyKCk7XG5jb25zdCB3YXJuID0gY3VzdG9tTG9nZ2VyLndhcm47XG5cbmN1c3RvbUxvZ2dlci53YXJuID0gKG1zZywgb3B0aW9ucykgPT4ge1xuICBpZiAoXG4gICAgbXNnLmluY2x1ZGVzKGBNb2R1bGUgXCJmc1wiIGhhcyBiZWVuIGV4dGVybmFsaXplZGApICYmXG4gICAgbXNnLmluY2x1ZGVzKFwibm9kZV9tb2R1bGVzL2VsZWN0cm9uL2luZGV4LmpzXCIpXG4gIClcbiAgICByZXR1cm47XG4gIGlmIChcbiAgICBtc2cuaW5jbHVkZXMoYE1vZHVsZSBcInBhdGhcIiBoYXMgYmVlbiBleHRlcm5hbGl6ZWRgKSAmJlxuICAgIG1zZy5pbmNsdWRlcyhcIm5vZGVfbW9kdWxlcy9lbGVjdHJvbi9pbmRleC5qc1wiKVxuICApXG4gICAgcmV0dXJuO1xuICB3YXJuKG1zZywgb3B0aW9ucyk7XG59O1xuXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKCh7IG1vZGUgfSkgPT4ge1xuICBjb25zdCBlbnYgPSBsb2FkRW52KG1vZGUsIHByb2Nlc3MuY3dkKCksIFwiXCIpO1xuICBjb25zdCBiaW1EaXIgPSBlbnYuSU1KU19CSU1fRElSO1xuICBjb25zdCBmaWxlcyA9IGJpbURpciA/IGZzLnJlYWRkaXJTeW5jKGJpbURpcikgOiBbXTtcbiAgY29uc3QgYmltRmlsZXMgPSBmaWxlcy5maWx0ZXIoXG4gICAgKGZpbGUpID0+IGZpbGUuZW5kc1dpdGgoXCIuYmltXCIpIHx8IGZpbGUuZW5kc1dpdGgoXCIuaWJpbVwiKVxuICApO1xuICByZXR1cm4ge1xuICAgIGJ1aWxkOiB7XG4gICAgICBjaHVua1NpemVXYXJuaW5nTGltaXQ6IDcwMDAsXG4gICAgfSxcbiAgICBjdXN0b21Mb2dnZXIsXG4gICAgY3NzOiB7XG4gICAgICBwcmVwcm9jZXNzb3JPcHRpb25zOiB7XG4gICAgICAgIHNjc3M6IHtcbiAgICAgICAgICBhcGk6IFwibW9kZXJuLWNvbXBpbGVyXCIsXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH0sXG4gICAgcGx1Z2luczogW1xuICAgICAgVGFuU3RhY2tSb3V0ZXJWaXRlKCksXG4gICAgICByZWFjdCgpLFxuICAgICAgdml0ZVN0YXRpY0NvcHkoe1xuICAgICAgICB0YXJnZXRzOiBbXG4gICAgICAgICAge1xuICAgICAgICAgICAgLy8gY29weSBhc3NldHMgZnJvbSBgQGl0d2luYCBkZXBlbmRlbmNpZXNcbiAgICAgICAgICAgIHNyYzogXCIuL25vZGVfbW9kdWxlcy9AaXR3aW4vKi9saWIvcHVibGljLypcIixcbiAgICAgICAgICAgIGRlc3Q6IFwiLlwiLFxuICAgICAgICAgIH0sXG4gICAgICAgICAge1xuICAgICAgICAgICAgLy8gY29weSBsb2NhbGl6YXRpb24gZmlsZXNcbiAgICAgICAgICAgIHNyYzogXCIuL2xpYi9sb2NhbGVzXCIsXG4gICAgICAgICAgICBkZXN0OiBcIi5cIixcbiAgICAgICAgICB9LFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIHNyYzogXCIuL2xpYi9sb2NhbGVzL2VuLyoqXCIsXG4gICAgICAgICAgICBkZXN0OiBcIi4vbG9jYWxlcy9lbi1VU1wiLFxuICAgICAgICAgIH0sXG4gICAgICAgIF0sXG4gICAgICB9KSxcbiAgICAgIHtcbiAgICAgICAgbmFtZTogXCJtYXJrZG93bi1sb2FkZXJcIixcbiAgICAgICAgdHJhbnNmb3JtKGNvZGUsIGlkKSB7XG4gICAgICAgICAgaWYgKCFpZC5lbmRzV2l0aChcIi5tZFwiKSkgcmV0dXJuO1xuXG4gICAgICAgICAgLy8gRm9yIC5tZCBmaWxlcywgZ2V0IHRoZSByYXcgY29udGVudFxuICAgICAgICAgIHJldHVybiBgZXhwb3J0IGRlZmF1bHQgJHtKU09OLnN0cmluZ2lmeShjb2RlKX07YDtcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgXSxcbiAgICByZXNvbHZlOiB7XG4gICAgICBhbGlhczogW1xuICAgICAgICB7XG4gICAgICAgICAgZmluZDogXCJ+QGl0d2luL2FwcHVpLXJlYWN0XCIsXG4gICAgICAgICAgcmVwbGFjZW1lbnQ6IFwiQGl0d2luL2FwcHVpLXJlYWN0XCIsXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBmaW5kOiBcIn5AaXR3aW4vY29yZS1yZWFjdFwiLFxuICAgICAgICAgIHJlcGxhY2VtZW50OiBcIkBpdHdpbi9jb3JlLXJlYWN0XCIsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH0sXG4gICAgZW52UHJlZml4OiBcIklNSlNfXCIsXG4gICAgc2VydmVyOiB7XG4gICAgICBwb3J0OiAzMDAwLFxuICAgICAgc3RyaWN0UG9ydDogdHJ1ZSxcbiAgICB9LFxuICAgIGRlZmluZToge1xuICAgICAgX19CSU1fRklMRVNfXzogSlNPTi5zdHJpbmdpZnkoYmltRmlsZXMpLFxuICAgIH0sXG4gIH07XG59KTtcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFJQSxPQUFPLFFBQVE7QUFDZixTQUFTLGNBQWMsY0FBYyxlQUFlO0FBQ3BELE9BQU8sV0FBVztBQUNsQixTQUFTLHNCQUFzQjtBQUMvQixTQUFTLDBCQUEwQjtBQUVuQyxJQUFNLGVBQWUsYUFBYTtBQUNsQyxJQUFNLE9BQU8sYUFBYTtBQUUxQixhQUFhLE9BQU8sQ0FBQyxLQUFLLFlBQVk7QUFDcEMsTUFDRSxJQUFJLFNBQVMsbUNBQW1DLEtBQ2hELElBQUksU0FBUyxnQ0FBZ0M7QUFFN0M7QUFDRixNQUNFLElBQUksU0FBUyxxQ0FBcUMsS0FDbEQsSUFBSSxTQUFTLGdDQUFnQztBQUU3QztBQUNGLE9BQUssS0FBSyxPQUFPO0FBQ25CO0FBR0EsSUFBTyxzQkFBUSxhQUFhLENBQUMsRUFBRSxLQUFLLE1BQU07QUFDeEMsUUFBTSxNQUFNLFFBQVEsTUFBTSxRQUFRLElBQUksR0FBRyxFQUFFO0FBQzNDLFFBQU0sU0FBUyxJQUFJO0FBQ25CLFFBQU0sUUFBUSxTQUFTLEdBQUcsWUFBWSxNQUFNLElBQUksQ0FBQztBQUNqRCxRQUFNLFdBQVcsTUFBTTtBQUFBLElBQ3JCLENBQUMsU0FBUyxLQUFLLFNBQVMsTUFBTSxLQUFLLEtBQUssU0FBUyxPQUFPO0FBQUEsRUFDMUQ7QUFDQSxTQUFPO0FBQUEsSUFDTCxPQUFPO0FBQUEsTUFDTCx1QkFBdUI7QUFBQSxJQUN6QjtBQUFBLElBQ0E7QUFBQSxJQUNBLEtBQUs7QUFBQSxNQUNILHFCQUFxQjtBQUFBLFFBQ25CLE1BQU07QUFBQSxVQUNKLEtBQUs7QUFBQSxRQUNQO0FBQUEsTUFDRjtBQUFBLElBQ0Y7QUFBQSxJQUNBLFNBQVM7QUFBQSxNQUNQLG1CQUFtQjtBQUFBLE1BQ25CLE1BQU07QUFBQSxNQUNOLGVBQWU7QUFBQSxRQUNiLFNBQVM7QUFBQSxVQUNQO0FBQUE7QUFBQSxZQUVFLEtBQUs7QUFBQSxZQUNMLE1BQU07QUFBQSxVQUNSO0FBQUEsVUFDQTtBQUFBO0FBQUEsWUFFRSxLQUFLO0FBQUEsWUFDTCxNQUFNO0FBQUEsVUFDUjtBQUFBLFVBQ0E7QUFBQSxZQUNFLEtBQUs7QUFBQSxZQUNMLE1BQU07QUFBQSxVQUNSO0FBQUEsUUFDRjtBQUFBLE1BQ0YsQ0FBQztBQUFBLE1BQ0Q7QUFBQSxRQUNFLE1BQU07QUFBQSxRQUNOLFVBQVUsTUFBTSxJQUFJO0FBQ2xCLGNBQUksQ0FBQyxHQUFHLFNBQVMsS0FBSyxFQUFHO0FBR3pCLGlCQUFPLGtCQUFrQixLQUFLLFVBQVUsSUFBSSxDQUFDO0FBQUEsUUFDL0M7QUFBQSxNQUNGO0FBQUEsSUFDRjtBQUFBLElBQ0EsU0FBUztBQUFBLE1BQ1AsT0FBTztBQUFBLFFBQ0w7QUFBQSxVQUNFLE1BQU07QUFBQSxVQUNOLGFBQWE7QUFBQSxRQUNmO0FBQUEsUUFDQTtBQUFBLFVBQ0UsTUFBTTtBQUFBLFVBQ04sYUFBYTtBQUFBLFFBQ2Y7QUFBQSxNQUNGO0FBQUEsSUFDRjtBQUFBLElBQ0EsV0FBVztBQUFBLElBQ1gsUUFBUTtBQUFBLE1BQ04sTUFBTTtBQUFBLE1BQ04sWUFBWTtBQUFBLElBQ2Q7QUFBQSxJQUNBLFFBQVE7QUFBQSxNQUNOLGVBQWUsS0FBSyxVQUFVLFFBQVE7QUFBQSxJQUN4QztBQUFBLEVBQ0Y7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo= From 01442d28d66b6485caab2fadce793f4c24124ec4 Mon Sep 17 00:00:00 2001 From: mathieufournier Date: Fri, 13 Dec 2024 17:17:00 -0500 Subject: [PATCH 04/15] Removed the espace unloack all and the check for dynamics when changing accudraw values --- .../appui-react/accudraw/AccuDrawFieldContainer.tsx | 1 - .../src/appui-react/accudraw/FrameworkAccuDraw.ts | 11 +++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx b/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx index 5ecbdbf1097..4c6d1c9e55b 100644 --- a/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx +++ b/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx @@ -236,7 +236,6 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { }, [showZ]); const handleEscPressed = React.useCallback(() => { - IModelApp.accuDraw.unlockAllFields(); UiFramework.keyboardShortcuts.setFocusToHome(); }, []); diff --git a/ui/appui-react/src/appui-react/accudraw/FrameworkAccuDraw.ts b/ui/appui-react/src/appui-react/accudraw/FrameworkAccuDraw.ts index 634d116a210..cacfea7fdb7 100644 --- a/ui/appui-react/src/appui-react/accudraw/FrameworkAccuDraw.ts +++ b/ui/appui-react/src/appui-react/accudraw/FrameworkAccuDraw.ts @@ -13,7 +13,6 @@ import { CompassMode, IModelApp, ItemField, - KeyinStatus, NotifyMessageDetails, OutputMessagePriority, QuantityType, @@ -309,11 +308,11 @@ export class FrameworkAccuDraw private fieldValuesChanged(): void { // Only change the value when in Dynamic mode. Other mode are "DontUpdate" when the input is locked and "Partial" when the user is typing. - (this.getKeyinStatus(ItemField.X_Item) === KeyinStatus.Dynamic) && this.onFieldValueChange(ItemField.X_Item); - (this.getKeyinStatus(ItemField.Y_Item) === KeyinStatus.Dynamic) && this.onFieldValueChange(ItemField.Y_Item); - (this.getKeyinStatus(ItemField.Z_Item) === KeyinStatus.Dynamic) && this.onFieldValueChange(ItemField.Z_Item); - (this.getKeyinStatus(ItemField.ANGLE_Item) === KeyinStatus.Dynamic) && this.onFieldValueChange(ItemField.ANGLE_Item); - (this.getKeyinStatus(ItemField.DIST_Item) === KeyinStatus.Dynamic) && this.onFieldValueChange(ItemField.DIST_Item); + this.onFieldValueChange(ItemField.X_Item); + this.onFieldValueChange(ItemField.Y_Item); + this.onFieldValueChange(ItemField.Z_Item); + this.onFieldValueChange(ItemField.ANGLE_Item); + this.onFieldValueChange(ItemField.DIST_Item); } public override setFocusItem(index: ItemField): void { From d100a491c22c206040f0235e6aad88256642e810 Mon Sep 17 00:00:00 2001 From: mathieufournier Date: Fri, 3 Jan 2025 09:27:55 -0500 Subject: [PATCH 05/15] fixed the accudraw highlight bug and pr comments --- ...ccudraw_update_fixes_2024-12-13-20-55.json | 4 +- docs/changehistory/NextVersion.md | 13 +++++++ .../accudraw/AccuDrawFieldContainer.tsx | 39 +++---------------- .../accudraw/AccuDrawInputField.tsx | 19 ++++++--- 4 files changed, 33 insertions(+), 42 deletions(-) diff --git a/common/changes/@itwin/appui-react/accudraw_update_fixes_2024-12-13-20-55.json b/common/changes/@itwin/appui-react/accudraw_update_fixes_2024-12-13-20-55.json index bbc1d7fa693..153e9f2e0de 100644 --- a/common/changes/@itwin/appui-react/accudraw_update_fixes_2024-12-13-20-55.json +++ b/common/changes/@itwin/appui-react/accudraw_update_fixes_2024-12-13-20-55.json @@ -2,9 +2,9 @@ "changes": [ { "packageName": "@itwin/appui-react", - "comment": "Accudraw input fields are now focused when the accudraw input field container becomes visible and when the compass mode changes. Fix : The focus does not change between X or Y when in polar mode. Now only updating input fields value when their state is dynamic. Made rectangular inputs color standard to iTwinUI. We can enter letters in the Bearing angle input field. Bearing angle input field automatically adds special characters to facilitate entering bearing angles. Removed the delay after typing in the input field, the visual update should be immediate. The Focus is now trapped in Accudraw input fields, when doing Tab. To focus out of these fields, the user must press Escape. Escape now unlocks all fields. ", + "comment": "Fixed some AccuDraw behaviours that where working incorrectly, like input highlight, focus and update. Improved typing Bearing angles.", "type": "none" } ], "packageName": "@itwin/appui-react" -} \ No newline at end of file +} diff --git a/docs/changehistory/NextVersion.md b/docs/changehistory/NextVersion.md index ef9e26c1d2e..53abbf8aa7e 100644 --- a/docs/changehistory/NextVersion.md +++ b/docs/changehistory/NextVersion.md @@ -13,6 +13,19 @@ - Specified additional version ranges in redux related peer dependencies. `redux` version is updated from `^4.1.0` to `^4.1.0 || ^5.0.0` and `react-redux` version is updated from `^7.2.2` to `^7.2.2 || ^8.0.0 || ^9.0.0`. This enables consumers to utilize latest redux capabilities. See [redux release v5.0.0](https://github.com/reduxjs/redux/releases/tag/v5.0.0) for migration tips. [#1151](https://github.com/iTwin/appui/pull/1151) +### AccuDraw Fixes + +- When moving the mouse, the focus change between X or Y, in rectangular mode only. +- Removed the delay after typing in the input field, so the visual update is immediate. +- Input fields are automatically focused when the AccuDrawFieldContainer appears and the the compass mode changes. + +### AccuDraw Additions + +- Rectangular inputs no longer have colors. +- We can enter letters in the Bearing angle input field. `N, S, E, W` for North, South, East and West. +- Bearing angle input field automatically adds special characters `° ' "` to facilitate entering bearing angles. +- The Focus is now trapped in Accudraw input fields. To focus out of these fields, the user must press Escape. + ## @itwin/components-react ### Additions diff --git a/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx b/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx index 4c6d1c9e55b..e982eab79ca 100644 --- a/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx +++ b/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx @@ -154,13 +154,6 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { [getInputToFocus] ); - React.useEffect(()=>{ - // Set the focus to the first input field when the component is mounted and when the compass mode changes. - const itemToFocus = mode === CompassMode.Polar ? ItemField.DIST_Item : ItemField.X_Item; - IModelApp.accuDraw.setFocusItem(itemToFocus); - setFocusToField(itemToFocus); - },[mode, setFocusToField, getInputToFocus]); - React.useEffect(() => { return FrameworkAccuDraw.onAccuDrawSetFieldLockEvent.addListener((args) => { switch (args.field) { @@ -213,28 +206,6 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { [] ); - const focusNextField = React.useCallback((field: ItemField) => { - switch (field) { - case ItemField.X_Item: - IModelApp.accuDraw.setFocusItem(ItemField.Y_Item); - break; - case ItemField.Y_Item: - showZ ? - IModelApp.accuDraw.setFocusItem(ItemField.Z_Item) : - IModelApp.accuDraw.setFocusItem(ItemField.X_Item); - break; - case ItemField.Z_Item: - IModelApp.accuDraw.setFocusItem(ItemField.X_Item); - break; - case ItemField.ANGLE_Item: - IModelApp.accuDraw.setFocusItem(ItemField.DIST_Item); - break; - case ItemField.DIST_Item: - IModelApp.accuDraw.setFocusItem(ItemField.ANGLE_Item); - break; - } - }, [showZ]); - const handleEscPressed = React.useCallback(() => { UiFramework.keyboardShortcuts.setFocusToHome(); }, []); @@ -358,7 +329,7 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { handleValueChanged(ItemField.X_Item, stringValue) } onEscPressed={handleEscPressed} - onTabPressed={focusNextField} + onTabPressed={()=>IModelApp.accuDraw.setFocusItem(ItemField.Y_Item)} /> showZ ? IModelApp.accuDraw.setFocusItem(ItemField.Z_Item) : IModelApp.accuDraw.setFocusItem(ItemField.X_Item)} /> {showZ && ( IModelApp.accuDraw.setFocusItem(ItemField.X_Item)} /> )} @@ -417,7 +388,7 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { handleValueChanged(ItemField.DIST_Item, stringValue) } onEscPressed={handleEscPressed} - onTabPressed={focusNextField} + onTabPressed={()=>IModelApp.accuDraw.setFocusItem(ItemField.ANGLE_Item)} /> IModelApp.accuDraw.setFocusItem(ItemField.DIST_Item)} /> )} diff --git a/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx b/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx index 6f467e16661..2095f1a2552 100644 --- a/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx +++ b/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx @@ -10,7 +10,7 @@ import "./AccuDrawInputField.scss"; import classnames from "classnames"; import * as React from "react"; import { Key } from "ts-key-enum"; -import type { ItemField } from "@itwin/core-frontend"; +import { ItemField } from "@itwin/core-frontend"; import type { CommonProps, IconSpec } from "@itwin/core-react"; import { Icon } from "@itwin/core-react"; import { useRefs } from "@itwin/core-react/internal"; @@ -60,7 +60,7 @@ export interface AccuDrawInputFieldProps extends CommonProps { /** Listens for keypress */ onEscPressed?: () => void; /** Listens for keypress */ - onTabPressed?: (field: ItemField) => void; + onTabPressed?: () => void; /** Provides ability to return reference to HTMLInputElement */ ref?: React.Ref; } @@ -94,8 +94,15 @@ const ForwardRefAccuDrawInput = React.forwardRef< const [stringValue, setStringValue] = React.useState(""); const [needSelection, setNeedSelection] = React.useState(false); const [isFocusField, setIsFocusField] = React.useState(false); - const inputElementRef = React.useRef(null); - const refs = useRefs(inputElementRef, ref); // combine ref needed for target with the forwardRef needed by the Parent when parent is a Type Editor. + const inputElementRef = React.useRef(null); + const refs = useRefs((instance: HTMLInputElement | null) => { + if(instance !== null && (field === ItemField.DIST_Item || field === ItemField.X_Item)){ + // Focus the X or Distance input when it's mounted. This also happens when the compass mode changes. + instance.focus() + instance.select() + } + inputElementRef.current = instance; + }, ref); // combine ref needed for target with the forwardRef needed by the Parent when parent is a Type Editor. const allowBearingLettersInAccuDrawInputFields = useAllowBearingLettersInAccuDrawInputFields(); @@ -147,7 +154,7 @@ const ForwardRefAccuDrawInput = React.forwardRef< onEnterPressed && onEnterPressed(); return; case Key.Tab.valueOf(): - onTabPressed && onTabPressed(field); + onTabPressed && onTabPressed(); // Prevent from focusing other elements in the page. Act like a focus trap. e.preventDefault(); return; @@ -169,7 +176,7 @@ const ForwardRefAccuDrawInput = React.forwardRef< ); } }, - [onEscPressed, onEnterPressed, onTabPressed, isBearingAngle, allowBearingLettersInAccuDrawInputFields, field] + [onEscPressed, onEnterPressed, onTabPressed, isBearingAngle, allowBearingLettersInAccuDrawInputFields] ); React.useEffect(() => { From 4e48f00b499a9c3d499b1223dbad288d404cb3a0 Mon Sep 17 00:00:00 2001 From: mathieufournier Date: Fri, 3 Jan 2025 10:47:39 -0500 Subject: [PATCH 06/15] ran prettier --- .../accudraw/AccuDrawFieldContainer.tsx | 22 +++++++--- .../accudraw/AccuDrawInputField.tsx | 40 +++++++++++-------- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx b/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx index e982eab79ca..2df20ac7b30 100644 --- a/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx +++ b/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx @@ -329,7 +329,9 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { handleValueChanged(ItemField.X_Item, stringValue) } onEscPressed={handleEscPressed} - onTabPressed={()=>IModelApp.accuDraw.setFocusItem(ItemField.Y_Item)} + onTabPressed={() => + IModelApp.accuDraw.setFocusItem(ItemField.Y_Item) + } /> showZ ? IModelApp.accuDraw.setFocusItem(ItemField.Z_Item) : IModelApp.accuDraw.setFocusItem(ItemField.X_Item)} + onTabPressed={() => + showZ + ? IModelApp.accuDraw.setFocusItem(ItemField.Z_Item) + : IModelApp.accuDraw.setFocusItem(ItemField.X_Item) + } /> {showZ && ( IModelApp.accuDraw.setFocusItem(ItemField.X_Item)} + onTabPressed={() => + IModelApp.accuDraw.setFocusItem(ItemField.X_Item) + } /> )} @@ -388,7 +396,9 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { handleValueChanged(ItemField.DIST_Item, stringValue) } onEscPressed={handleEscPressed} - onTabPressed={()=>IModelApp.accuDraw.setFocusItem(ItemField.ANGLE_Item)} + onTabPressed={() => + IModelApp.accuDraw.setFocusItem(ItemField.ANGLE_Item) + } /> IModelApp.accuDraw.setFocusItem(ItemField.DIST_Item)} + onTabPressed={() => + IModelApp.accuDraw.setFocusItem(ItemField.DIST_Item) + } /> )} diff --git a/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx b/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx index 2095f1a2552..eb486174d1b 100644 --- a/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx +++ b/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx @@ -96,15 +96,19 @@ const ForwardRefAccuDrawInput = React.forwardRef< const [isFocusField, setIsFocusField] = React.useState(false); const inputElementRef = React.useRef(null); const refs = useRefs((instance: HTMLInputElement | null) => { - if(instance !== null && (field === ItemField.DIST_Item || field === ItemField.X_Item)){ + if ( + instance !== null && + (field === ItemField.DIST_Item || field === ItemField.X_Item) + ) { // Focus the X or Distance input when it's mounted. This also happens when the compass mode changes. - instance.focus() - instance.select() + instance.focus(); + instance.select(); } inputElementRef.current = instance; }, ref); // combine ref needed for target with the forwardRef needed by the Parent when parent is a Type Editor. - const allowBearingLettersInAccuDrawInputFields = useAllowBearingLettersInAccuDrawInputFields(); + const allowBearingLettersInAccuDrawInputFields = + useAllowBearingLettersInAccuDrawInputFields(); React.useEffect(() => { const formattedValue = FrameworkAccuDraw.getFieldDisplayValue(field); @@ -118,19 +122,17 @@ const ForwardRefAccuDrawInput = React.forwardRef< if (value === undefined) return; if (stringValue !== value) { - if(isBearingAngle){ + if (isBearingAngle) { // Parsing bearing only works properly when parsing UpperCase letters. Ex : S45°00'00"E <- ok, s45°00'00"e <- doesnt work. value = value.toUpperCase(); - if(value.length > stringValue.length){ + if (value.length > stringValue.length) { // Automatically add Bearing special characters to help users type the angle. Ex: S45°00'00"E (degrees `°`, minutes `'`, seconds `"`) - if(value.length === 3 && value[2] !== "°"){ + if (value.length === 3 && value[2] !== "°") { value += "°"; - } - else if(value.length === 6 && value[5] !== "'"){ + } else if (value.length === 6 && value[5] !== "'") { value += "'"; - } - else if(value.length === 9 && value[8] !== "\""){ - value += "\""; + } else if (value.length === 9 && value[8] !== '"') { + value += '"'; } } } @@ -161,9 +163,9 @@ const ForwardRefAccuDrawInput = React.forwardRef< } if (isLetter(e.key)) { - if(isBearingAngle && allowBearingLettersInAccuDrawInputFields){ - const bearingReservedKeys = ['n', 's', 'e', 'w', 'N', 'S', 'E', 'W']; - if(bearingReservedKeys.includes(e.key)){ + if (isBearingAngle && allowBearingLettersInAccuDrawInputFields) { + const bearingReservedKeys = ["n", "s", "e", "w", "N", "S", "E", "W"]; + if (bearingReservedKeys.includes(e.key)) { return; // The letter is displayed in the Bearing input field. } } @@ -176,7 +178,13 @@ const ForwardRefAccuDrawInput = React.forwardRef< ); } }, - [onEscPressed, onEnterPressed, onTabPressed, isBearingAngle, allowBearingLettersInAccuDrawInputFields] + [ + onEscPressed, + onEnterPressed, + onTabPressed, + isBearingAngle, + allowBearingLettersInAccuDrawInputFields, + ] ); React.useEffect(() => { From 3d99ce0f942c24d2701c98c77d3df3e3d746f6d9 Mon Sep 17 00:00:00 2001 From: mathieufournier Date: Fri, 3 Jan 2025 11:04:12 -0500 Subject: [PATCH 07/15] updated the api --- common/api/appui-react.api.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/api/appui-react.api.md b/common/api/appui-react.api.md index fa4faa51f21..99963b81fe2 100644 --- a/common/api/appui-react.api.md +++ b/common/api/appui-react.api.md @@ -216,7 +216,7 @@ export interface AccuDrawInputFieldProps extends CommonProps { labelStyle?: React_2.CSSProperties; onEnterPressed?: () => void; onEscPressed?: () => void; - onTabPressed?: (field: ItemField) => void; + onTabPressed?: () => void; onValueChanged: (stringValue: string) => void; ref?: React_2.Ref; } @@ -2069,7 +2069,7 @@ export interface FrameworkKeyboardShortcuts { export const FrameworkReducer: Reducer_2< { configurableUiState: ConfigurableUiState; sessionState: DeepReadonlyObject_2; -}, SessionStateActionsUnion_2 | ConfigurableUiActionsUnion_2, Partial<{ +}, ConfigurableUiActionsUnion_2 | SessionStateActionsUnion_2, Partial<{ configurableUiState: never; sessionState: never; }>>; @@ -5353,7 +5353,7 @@ export function useUiStateStorageHandler(): UiStateStorage; // @alpha export function useWidget(): { state: WidgetState; - widgetLocation: "popout" | "floating" | "docked"; + widgetLocation: "docked" | "floating" | "popout"; setState: (widgetState: Omit) => void; }; From c389f599f1f8146255a3f9cc79728f8077fd8a2b Mon Sep 17 00:00:00 2001 From: mathieufournier Date: Fri, 3 Jan 2025 14:58:40 -0500 Subject: [PATCH 08/15] Fixed typing in the input field plus the highlight. --- .../accudraw/AccuDrawFieldContainer.tsx | 21 +++++++++++++++++++ .../accudraw/AccuDrawInputField.tsx | 14 ++----------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx b/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx index 2df20ac7b30..528c07b270a 100644 --- a/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx +++ b/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx @@ -154,6 +154,27 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { [getInputToFocus] ); + React.useEffect(() => { + // Set the focus to the first input field when the component is mounted and when the compass mode changes. + const itemToFocus = + mode === CompassMode.Polar ? ItemField.DIST_Item : ItemField.X_Item; + IModelApp.accuDraw.setFocusItem(itemToFocus); + setFocusToField(itemToFocus); + + const timeoutId = setTimeout(() => { + // Timeout to force an highlight on the field. + const inputToFocus = + mode === CompassMode.Rectangular + ? getFieldInput(ItemField.X_Item) + : getFieldInput(ItemField.DIST_Item); + inputToFocus && inputToFocus.focus(); + inputToFocus && inputToFocus.select(); + }, 1); + return () => { + clearTimeout(timeoutId); + }; + }, [mode, setFocusToField]); + React.useEffect(() => { return FrameworkAccuDraw.onAccuDrawSetFieldLockEvent.addListener((args) => { switch (args.field) { diff --git a/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx b/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx index eb486174d1b..6122758c86b 100644 --- a/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx +++ b/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx @@ -10,7 +10,7 @@ import "./AccuDrawInputField.scss"; import classnames from "classnames"; import * as React from "react"; import { Key } from "ts-key-enum"; -import { ItemField } from "@itwin/core-frontend"; +import type { ItemField } from "@itwin/core-frontend"; import type { CommonProps, IconSpec } from "@itwin/core-react"; import { Icon } from "@itwin/core-react"; import { useRefs } from "@itwin/core-react/internal"; @@ -95,17 +95,7 @@ const ForwardRefAccuDrawInput = React.forwardRef< const [needSelection, setNeedSelection] = React.useState(false); const [isFocusField, setIsFocusField] = React.useState(false); const inputElementRef = React.useRef(null); - const refs = useRefs((instance: HTMLInputElement | null) => { - if ( - instance !== null && - (field === ItemField.DIST_Item || field === ItemField.X_Item) - ) { - // Focus the X or Distance input when it's mounted. This also happens when the compass mode changes. - instance.focus(); - instance.select(); - } - inputElementRef.current = instance; - }, ref); // combine ref needed for target with the forwardRef needed by the Parent when parent is a Type Editor. + const refs = useRefs(inputElementRef, ref); // combine ref needed for target with the forwardRef needed by the Parent when parent is a Type Editor. const allowBearingLettersInAccuDrawInputFields = useAllowBearingLettersInAccuDrawInputFields(); From b4de51e12a6b75ca707f158a11ac81db36207579 Mon Sep 17 00:00:00 2001 From: mathieufournier Date: Fri, 3 Jan 2025 15:35:38 -0500 Subject: [PATCH 09/15] fixed tests --- .../accudraw/AccuDrawFieldContainer.test.tsx | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/ui/appui-react/src/test/accudraw/AccuDrawFieldContainer.test.tsx b/ui/appui-react/src/test/accudraw/AccuDrawFieldContainer.test.tsx index 05070a7d7b6..9715a8ea5a1 100644 --- a/ui/appui-react/src/test/accudraw/AccuDrawFieldContainer.test.tsx +++ b/ui/appui-react/src/test/accudraw/AccuDrawFieldContainer.test.tsx @@ -93,14 +93,14 @@ describe("AccuDrawFieldContainer", () => { const wrapper = render( ); - expect(IModelApp.accuDraw.hasInputFocus).toEqual(false); + expect(IModelApp.accuDraw.hasInputFocus).toEqual(true); act(() => { IModelApp.accuDraw.setCompassMode(CompassMode.Rectangular); IModelApp.accuDraw.setFocusItem(ItemField.X_Item); }); - expect(spy).toHaveBeenCalledOnce(); + expect(spy).toHaveBeenCalled(); let input = wrapper.queryByTestId("uifw-accudraw-x"); expect(input).toBeTruthy(); expect(document.activeElement).toEqual(input); @@ -125,7 +125,7 @@ describe("AccuDrawFieldContainer", () => { IModelApp.accuDraw.setFocusItem(ItemField.ANGLE_Item); }); - expect(spy).toHaveBeenCalledOnce(); + expect(spy).toHaveBeenCalled(); input = wrapper.queryByTestId("uifw-accudraw-angle"); expect(input).toBeTruthy(); expect(document.activeElement).toEqual(input); @@ -155,7 +155,7 @@ describe("AccuDrawFieldContainer", () => { showZOverride={true} /> ); - expect(IModelApp.accuDraw.hasInputFocus).toEqual(false); + expect(IModelApp.accuDraw.hasInputFocus).toEqual(true); IModelApp.accuDraw.setCompassMode(CompassMode.Rectangular); @@ -164,7 +164,7 @@ describe("AccuDrawFieldContainer", () => { }); IModelApp.accuDraw.setFocusItem(ItemField.Z_Item); - expect(spy).toHaveBeenCalledOnce(); + expect(spy).toHaveBeenCalled(); await TestUtils.flushAsyncOperations(); expect(document.activeElement).toEqual( screen.getByTestId("uifw-accudraw-z") @@ -183,13 +183,13 @@ describe("AccuDrawFieldContainer", () => { const wrapper = render( ); - expect(IModelApp.accuDraw.hasInputFocus).toEqual(false); + expect(IModelApp.accuDraw.hasInputFocus).toEqual(true); IModelApp.accuDraw.setCompassMode(CompassMode.Rectangular); await TestUtils.flushAsyncOperations(); IModelApp.accuDraw.setFocusItem(ItemField.X_Item); - expect(spySet).toHaveBeenCalledOnce(); + expect(spySet).toHaveBeenCalled(); const input = wrapper.queryByTestId("uifw-accudraw-x"); expect(input).toBeTruthy(); expect(document.activeElement).toEqual(input); @@ -232,23 +232,24 @@ describe("AccuDrawFieldContainer", () => { IModelApp.accuDraw.setCompassMode(CompassMode.Rectangular); + const inputKeys = "22.3"; await theUserTo.type( screen.getByTestId("uifw-accudraw-x"), - "22.3", + inputKeys, selectAllBeforeType() ); await waitFor(() => { - expect(spy).toHaveBeenCalledOnce(); + expect(spy).toHaveBeenCalledTimes(inputKeys.length); }); spy.mockReset(); await theUserTo.type( screen.getByTestId("uifw-accudraw-y"), - "22.3", + inputKeys, selectAllBeforeType() ); await waitFor(() => { - expect(spy).toHaveBeenCalledOnce(); + expect(spy).toHaveBeenCalledTimes(inputKeys.length); }); spy.mockReset(); @@ -259,22 +260,22 @@ describe("AccuDrawFieldContainer", () => { await waitFor(async () => { await theUserTo.type( screen.getByTestId("uifw-accudraw-angle"), - "22.3", + inputKeys, selectAllBeforeType() ); }); await waitFor(() => { - expect(spy).toHaveBeenCalledOnce(); + expect(spy).toHaveBeenCalledTimes(inputKeys.length); }); spy.mockReset(); await theUserTo.type( screen.getByTestId("uifw-accudraw-distance"), - "22.3", + inputKeys, selectAllBeforeType() ); await waitFor(() => { - expect(spy).toHaveBeenCalledOnce(); + expect(spy).toHaveBeenCalledTimes(inputKeys.length); }); spy.mockReset(); @@ -294,15 +295,16 @@ describe("AccuDrawFieldContainer", () => { IModelApp.accuDraw.setCompassMode(CompassMode.Rectangular); + const inputKeys = "22.3"; await waitFor(async () => { await theUserTo.type( screen.getByTestId("uifw-accudraw-z"), - "22.3", + inputKeys, selectAllBeforeType() ); }); await waitFor(() => { - expect(spy).toHaveBeenCalledOnce(); + expect(spy).toHaveBeenCalledTimes(inputKeys.length); }); remove(); From 6fd6f2c7d33c8794a8d594aa643ae15fd5a65a05 Mon Sep 17 00:00:00 2001 From: GerardasB <10091419+GerardasB@users.noreply.github.com> Date: Mon, 6 Jan 2025 12:40:46 +0200 Subject: [PATCH 10/15] Set-up editorToolSettings for editor tools. --- apps/test-app/src/routes/briefcase.lazy.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/test-app/src/routes/briefcase.lazy.tsx b/apps/test-app/src/routes/briefcase.lazy.tsx index b188e5cf86e..975f7821d4c 100644 --- a/apps/test-app/src/routes/briefcase.lazy.tsx +++ b/apps/test-app/src/routes/briefcase.lazy.tsx @@ -10,6 +10,7 @@ import { useSyncFrontstageParam, } from "../frontend/SearchParams"; import { createLazyFileRoute } from "@tanstack/react-router"; +import { useEditorToolSettings } from "../frontend/appui/useEditorToolSettings"; export const Route = createLazyFileRoute("/briefcase")({ component: Local, @@ -17,6 +18,7 @@ export const Route = createLazyFileRoute("/briefcase")({ function Local() { useSyncFrontstageParam(); + useEditorToolSettings(); const featureOverrides = useFeatureOverrideParams(); return ( From 91cde00d7df39c89088592672550881d85fc3925 Mon Sep 17 00:00:00 2001 From: GerardasB <10091419+GerardasB@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:04:40 +0200 Subject: [PATCH 11/15] Move only focus and select calls to setTimeout. --- .../accudraw/AccuDrawFieldContainer.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx b/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx index 528c07b270a..4bc646cfdf7 100644 --- a/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx +++ b/ui/appui-react/src/appui-react/accudraw/AccuDrawFieldContainer.tsx @@ -160,15 +160,15 @@ export function AccuDrawFieldContainer(props: AccuDrawFieldContainerProps) { mode === CompassMode.Polar ? ItemField.DIST_Item : ItemField.X_Item; IModelApp.accuDraw.setFocusItem(itemToFocus); setFocusToField(itemToFocus); - + const inputToFocus = + mode === CompassMode.Rectangular + ? getFieldInput(ItemField.X_Item) + : getFieldInput(ItemField.DIST_Item); + if (!inputToFocus) return; const timeoutId = setTimeout(() => { // Timeout to force an highlight on the field. - const inputToFocus = - mode === CompassMode.Rectangular - ? getFieldInput(ItemField.X_Item) - : getFieldInput(ItemField.DIST_Item); - inputToFocus && inputToFocus.focus(); - inputToFocus && inputToFocus.select(); + inputToFocus.focus(); + inputToFocus.select(); }, 1); return () => { clearTimeout(timeoutId); From fb3557783573af5e64a3516c85a8ef98b0b5ac53 Mon Sep 17 00:00:00 2001 From: GerardasB <10091419+GerardasB@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:16:04 +0200 Subject: [PATCH 12/15] Update NextVersion.md --- docs/changehistory/NextVersion.md | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/docs/changehistory/NextVersion.md b/docs/changehistory/NextVersion.md index e2c199232f6..d51bb1c4b65 100644 --- a/docs/changehistory/NextVersion.md +++ b/docs/changehistory/NextVersion.md @@ -1,20 +1,18 @@ # NextVersion - [@itwin/appui-react](#itwinappui-react) - - [AccuDraw Fixes](#accudraw-fixes) - - [AccuDraw Additions](#accudraw-additions) + - [Changes](#changes) ## @itwin/appui-react -### AccuDraw Fixes +### Changes -- When moving the mouse, the focus change between X or Y, in rectangular mode only. -- Removed the delay after typing in the input field, so the visual update is immediate. -- Input fields are automatically focused when the AccuDrawFieldContainer appears and the the compass mode changes. - -### AccuDraw Additions - -- Rectangular inputs no longer have colors. -- We can enter letters in the Bearing angle input field. `N, S, E, W` for North, South, East and West. -- Bearing angle input field automatically adds special characters `° ' "` to facilitate entering bearing angles. -- The Focus is now trapped in Accudraw input fields. To focus out of these fields, the user must press Escape. +- AccuDraw interaction changes. [#1157](https://github.com/iTwin/appui/pull/1157) + - When moving the mouse, the focus changes between `X` and `Y` input fields, in rectangular mode only. + - Removed the delay after typing in the input field, so the visual update is immediate. + - The first input field is automatically focused when the `AccuDrawFieldContainer` appears. + - The first input field is automatically focused when the compass mode is changed. + - Removed colors from rectangular inputs. + - `N`, `S`, `E`, `W` letters, which correspond to North, South, East, and West, can be entered in the bearing angle input field. + - The bearing angle input field automatically adds special characters `°`, `'`, and `"` to facilitate entering the bearing angle. + - The focus is now trapped in AccuDraw input fields. To focus out of these fields, the end user can press the `Escape` key. From edc6ff31870eb1a9385bdaaf45580cf0d2cbb233 Mon Sep 17 00:00:00 2001 From: GerardasB <10091419+GerardasB@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:18:05 +0200 Subject: [PATCH 13/15] extract-api --- common/api/appui-react.api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/api/appui-react.api.md b/common/api/appui-react.api.md index 99963b81fe2..72332746faf 100644 --- a/common/api/appui-react.api.md +++ b/common/api/appui-react.api.md @@ -2069,7 +2069,7 @@ export interface FrameworkKeyboardShortcuts { export const FrameworkReducer: Reducer_2< { configurableUiState: ConfigurableUiState; sessionState: DeepReadonlyObject_2; -}, ConfigurableUiActionsUnion_2 | SessionStateActionsUnion_2, Partial<{ +}, SessionStateActionsUnion_2 | ConfigurableUiActionsUnion_2, Partial<{ configurableUiState: never; sessionState: never; }>>; @@ -5353,7 +5353,7 @@ export function useUiStateStorageHandler(): UiStateStorage; // @alpha export function useWidget(): { state: WidgetState; - widgetLocation: "docked" | "floating" | "popout"; + widgetLocation: "docked" | "popout" | "floating"; setState: (widgetState: Omit) => void; }; From 04739c4f86a94c6b5d0b54e041e011507d59d033 Mon Sep 17 00:00:00 2001 From: GerardasB <10091419+GerardasB@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:19:11 +0200 Subject: [PATCH 14/15] Fix spelling --- .../appui-react/accudraw_update_fixes_2024-12-13-20-55.json | 2 +- ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx | 2 +- ui/appui-react/src/appui-react/accudraw/FrameworkAccuDraw.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/changes/@itwin/appui-react/accudraw_update_fixes_2024-12-13-20-55.json b/common/changes/@itwin/appui-react/accudraw_update_fixes_2024-12-13-20-55.json index 153e9f2e0de..7290b57aa07 100644 --- a/common/changes/@itwin/appui-react/accudraw_update_fixes_2024-12-13-20-55.json +++ b/common/changes/@itwin/appui-react/accudraw_update_fixes_2024-12-13-20-55.json @@ -2,7 +2,7 @@ "changes": [ { "packageName": "@itwin/appui-react", - "comment": "Fixed some AccuDraw behaviours that where working incorrectly, like input highlight, focus and update. Improved typing Bearing angles.", + "comment": "Fixed some AccuDraw behaviors that where working incorrectly, like input highlight, focus and update. Improved typing Bearing angles.", "type": "none" } ], diff --git a/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx b/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx index 6122758c86b..5b151d06558 100644 --- a/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx +++ b/ui/appui-react/src/appui-react/accudraw/AccuDrawInputField.tsx @@ -113,7 +113,7 @@ const ForwardRefAccuDrawInput = React.forwardRef< if (stringValue !== value) { if (isBearingAngle) { - // Parsing bearing only works properly when parsing UpperCase letters. Ex : S45°00'00"E <- ok, s45°00'00"e <- doesnt work. + // Parsing bearing only works properly when parsing UpperCase letters. Ex : S45°00'00"E <- ok, s45°00'00"e <- doesn't work. value = value.toUpperCase(); if (value.length > stringValue.length) { // Automatically add Bearing special characters to help users type the angle. Ex: S45°00'00"E (degrees `°`, minutes `'`, seconds `"`) diff --git a/ui/appui-react/src/appui-react/accudraw/FrameworkAccuDraw.ts b/ui/appui-react/src/appui-react/accudraw/FrameworkAccuDraw.ts index cacfea7fdb7..e7efd5e6828 100644 --- a/ui/appui-react/src/appui-react/accudraw/FrameworkAccuDraw.ts +++ b/ui/appui-react/src/appui-react/accudraw/FrameworkAccuDraw.ts @@ -331,7 +331,7 @@ export class FrameworkAccuDraw if (!this.dontMoveFocus && this.compassMode === CompassMode.Rectangular) { // Changes the focus between X and Y axis depending on the cursor location, in rectangular mode. - // Example : this.newfocus is Y when the cursor is closer to the Y axis. + // Example : this.newFocus is Y when the cursor is closer to the Y axis. this.setFocusItem(this.newFocus); } } From 2bdd39c7bdf2eb507e916763cf3af1aa4397ecdd Mon Sep 17 00:00:00 2001 From: GerardasB <10091419+GerardasB@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:36:02 +0200 Subject: [PATCH 15/15] Update snaps --- .../accudraw-dialog-test-1-chromium-linux.png | Bin 3904 -> 4234 bytes .../configurable-ui-test-1-chromium-linux.png | Bin 27714 -> 27868 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/e2e-tests/tests/accudraw/accudraw-dialog.test.ts-snapshots/accudraw-dialog-test-1-chromium-linux.png b/e2e-tests/tests/accudraw/accudraw-dialog.test.ts-snapshots/accudraw-dialog-test-1-chromium-linux.png index 7336528eb6553dd36e25f97ff617249df782435f..b877ebb4018d24df99433134de095fca2b4ffbff 100644 GIT binary patch delta 4047 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 literal 3904 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 ?_kc(~BAbVE1Ud3QNTYSTqYBW>l34KLsg=~Nw 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 literal 27714 zcmce;1z1&IyDz#7N<>fuL`piQCq`SKjQ9v35q>=8BZUO1;F6r*B`|^GJ z?0xRNd++DD=R4=F=lTDaf^)7p#~kB*e>IlRdkH~|JCE-m5C{xmp|?^9#LX-C?*r;h z_=#$uRfZZXb5<%#Nz zX3xD9;r8q(ZsM#n$l0>1{OEP2ks&I4Fv6rosPg(9;lp=&1Ku2Y{|~$c8_BJ$MvZmL zp}{Bcpi@y(F;~Ajb%npUs|EcpzD3#P>g`iiK67^c@mh9_{Q7(YMY^Y?p0{t` zF4{y^R<*Y;D-wktBMjHGfuVcmmUw{F?l*`c7I5D^j4($ew?X=rG`hlz-^ zrHDpYT3KZTBw`RSPn21ZF)}`k<$+W5(DaFMZ%tLhElr&rZDmwWR@f-#fBaNk?NY6a z{Et^jNlBqzw}gnuSe1j7tt~U@{7{Z^D5Ye8w|7r}f9SiD>(kH)o6KirXB%%$l*{aE zynF;la9Pd^H7rX978dSJyIpabj4`fmR@!apcE_p8$x#G{z=WF-eG2_IvHJ#*ziH)6?mYV8%y!|t|>__)fR%WK{%Gh8> zN5@oMU2Cf%3}(my&-kl)BYQQ|++*giU%xgdX+4v@k3gJzJ?xXTu-Li0ILjE%lFz%; z-&ZU(orH6vk*1`iFz9w*(QFN74QI+`d3^qiMhc^Sba*HyFMrihjLp}QBpCSg^7!Ji z+UX#?aCH6e-@na)!~%i>Hk%Wyr*E@ELPBn@!ic%+2J@Yc7HA2Yn9!1H|8~D|3;8bA zv;vk(`o6STj+d8LR+f>brlyY0LRTz19-YeZXy&D$wamcRgW6qt4nFU1-@e7Fx}l4T zVu)jK#jF2$;dnYPEFxklF7BU{gqJ!@uA$O)gH3BOyJ_gJ zf&c2!bPF9Et>Jj_vgwW=}=0ynp-KY!lxwZKg;b;n;RHb*B0 zkf>j+(R*Uy<45|0Qe3>zc@f&0J)HXM*Y4P0mdokkg|m*kcJebyx3&g$+x2Bud-Juh zTL?tXyS~D>(9qC~fKV=%Q|q;%oB~~Eu3D;IFDy-6-FN=67H3cY2c5Ko$;ee5Zc@XWooUfr;X?x)u24_!pmV&zG&Mexuvee{HFo2<_x70MYeR4jS zCC@^B`d&pvC7nQ6SU5*%-RUNpaCoTO0&pTT#ey zo|=s8b4Blnw%&`=7&CgsVj2>MX7$)}$CFJC{Vw_a8(gypSL@d(e2 z3TDO8moFp(0|VC9)-f@$f_{%O&IaW%#?z9Yb;oj6S}k?yb*m?IZ9XEhIqz-oT3A?! zii+a&^Lyv_h}FF3N#?8QPA;d#{l+riJoVZa)YPzQwhT-+ISmJf)!q6>!UqQi9^|T| zELxD9oh?^89ewlt_7Eqm!gk~D>f+%=+0Fnf5?6n}!{xc+z)pJ60Qsc~SQT8*FI zgdHpCr-oT9z5qfl>-lZcy0INDF7%t9|HzD}a1fgvc0?K&WW0X;y7FkVu&Bs*WU>3= ztkl%!iDySNx7*+3E5h*>%>p{gXA|WvXD540yRa z=O6r`TfOBOG&VNIkS3{KX?vift7~9D72ho{Exk5WUFq+?0ZYKo&tLidErhf1vsl+} zzP=d*Y;0^8!$ZDYYHDgE{#^CZ4~(k@Rco9hD|Rl9<{sUBhh7pC5>iF;EIB#(ZN6ut zW`RO>nq>UD&2bhpSNCpDw5KG;vkhLbR=8a;134}D@`EaNjdpXChjUd`t93VE>EhC= z{5bjhC4u+Dhu+TQ#%vQOa9d3-5!c|0F^-E$?oaYt9(y3M@Cnl=o z&%@l=ny#&N-G~Y;+#4(YDaKSRIjzOV=RENR{VDn}p|-ZRT&~&$cl?fyj!u>w4Y&QV z_pe{S%s<28sI=XD;yE&Ua5eo$xIPP(<8;wZsUv0O^*t+>hL}yV*)19$lej&9@q&56 zVEp_&Q)>B^)1oilCv@ET#YI$lnitovisOXJ1GN+-01% z4;>B7M3sS-g+*`nsMT984(p95&*NjYYDb5_$2uQBwtRdU$K|rSba_EePHurm_v2{Q zW_7+*o?gA8yb|;Ae8zB!D3MoaRFva!PaU+!<@Dz$D1t|MwYcRA)lP>nFY}d4O{bFH z*=%Vfr>2HSL=d>`^ewcb!P2_1x41E0JKd43x5gDaJ(S1oaw-GQwp!4C)xb12vyFz9 zs@#HX=dIY?h<1$WqSn4&k_~i0$}DDd$5ig^3$~jRCvMo3N{Y(VR8&g~i*alYuSrQS zb)}@p0ZmR&QeN5wT?}6lYEqu0d&4RgJBwx$Mzs$jQzwvMCxh zmT1~raynY?YHJe_6`d@#>+Vo26xb}vaY=vNKXDY>Fnb#bd2w;^`Sa(_+@@8bg`wec zlw5%k5fMk*v!6YW?TG{ROicRcSx^u)Vj&XT9^6s`6H9WW$9$*G@-A){r#Pf z7S*M*%gWwZZ#?FT&&$Xt*BUmREuZS5qnmSS>W*jG+DQ_0+nnB7Ue?wznKVa7+u7mw z!ceZ!Oi2ixZW|kG9o6Z(qR1az-xiNXCH=&U-t138Z|@yeTwLsb(6Q2wMoKB!#C#}V zbKJu#&r)imQH$z`N<%|~e2?%g(IhnIdOrRmk~bq2l1uw5()R)aYE34dIlIS&78Ztu z*$8b*@wF7x1`dgG-8exv}i&QxQQr?M21KYbJljnskRZIbF^Uuq6ZlqnTUS9N=@)ano%l$D;IKJ^0m zbAj{8?oWO%DLct{E~3Ye$G-D4XcnXs3^2`dqhz*=6bg((Ir3Qju2g1k(aqg(uCOMhro(9TTVXU8mwrnu)V z-ISiUuLz$WqoRo&9P|(M{xmeo;dHHlPC<9)j^VocyQV$I4ZPb(D{E`me0;;%bkx-3 zo*d5GGc%uVc?LD5j$!egDk?Ia)9JmA@GVpFfgn(&D8O~HH|cUFD2Ed;9UgOcU$z!Yuyfo z)px70>^bMqjiCwB>=slzDP!>|T-E%%iH3$YaB}PH2+x8@#lgX06{~)K-#S?Rkai?# zJ|jIn8%>pxl2ZQt`~1scbwG%6xe8rNdl(oPZc11q6FD}6!)d0B9D>hN#IIMR$5FLY z>E?hx6nH5VgPRD(XDh+bPOD zVt!#Eg8=FkiIKAU^{rd>cDISl*HJwrP#)h~>e{idq6{Qq;fSfxlk5$B_M^j%!YS&< z4{u|hMkcQK?pgD)~@D;3ykA{KHXv3f&D7eW1~N4sz| zkc9K8CluJhkpa58VG~zZwT8T@#mxd)1qFrm@hiPT;yysMD-61(lMgIj(b1i4jN5HZ zwYInCp<(2(U{+8)oWF9YbWq7{K{i=}rX60r1<Pp*ghJGYF}s~gX`xZ)~i=jZoX4a0+jKS)bc z)6>(NYGd)<#a)Y+K;kUG{%UPwlR@w$k&TpVOrCyMuqwaFHPuMp&|5_URUH&kM{48i@3i2@uGO| zhY#iDth3R~CjQ>$HZ}yC3k%C|@f10?c$}WC?DyBYx#8pE69*n0@67G*?>BXwHhg@U z-X7t~h~RWS-UfIupe@VJUfC^U3vH;szh6{DM1e-GuxYrh?ec^=Fa7!EFZImKE9ZW1 z!nF5Nmo4NCO6M%9A9yF6)yTfBzoN_;ZlEEU|(us#1_D zBsdtKL)%BT{bqvN+qxpxCQbCq@*4>$2X$jE5FjwE6?d`?a6OKAI0pPeMLsp!}R zX179-eQVhcmmA5&+WE;|7!FPZJ*7%bOi!`Va7K-5Lv>YDjUwV$N|@~ej>RNm+wX{E z@C@pVX4x7m78RRIy`3=adgfp8O7jnD9hPj?nu!MQ1#YbcnZ>-&Rx0;t`7WUszfaJl{dFiEdxW zRVmP1JKT7Rg71cj$?d%SgmgYDZ@-X>XFPS#9*L~nh$(7Hzqt1RBc2L%A z0ax?IrH^Fco@rZXhu$+KF6so2cU6`;G{iI;L)A;-2_hX5wbm{ikUnVXfAXlQg(>24MV+t+>Ub2y5tM$78 znFln~4?tJ=_Kgb{7gsQVIQET!Wp~_Sec>)^OvTs05WhuNn0GI>Dnmm0O8LDuN6L-N z=3Q5hYXCW6zj{SG^#tLJpB&^0ZL0R_(rNW@u``<6aoxz=eEVqHja#oPMzK&AjlLFE zZ@61GCA;ZFS<^iR1DpT}i2+`IxB!w^k0)ZXvT(V6Se7ib;vG~0eNxgH1W>(<{2ilL zTSMQeJJ)?+P35y{P1&`E29&{Hw>44T|1HhuK_G9kxyX-OZkNLT4oJwz;=m!zyzY^E zu1^$P03|$7qL{7_p8WB8P+l8FSwcdh;Y&#G#C7>Uaw#n;nkaU%YYrenBOT6DKl_t& zgU5;T%Rax&)qB0UnH_sz-Xzsr@*h9WBnw}$Hec;6b>q>iiFH;ovalTO?VV*KwYRpO zT#T0%X7 z?hE|&%hKNdjm_rR-@lsN+_mGSW-J%8t*xz>mzM*6N~)@p+uP>kDS=|M=hgsR*;(18 z_1Gerv`$+?3;ZvL0sqm^UtRGlz4O1!l}?lJ4BGCDeoVZqtfq$h;DK1|4{d;)-RI9% z=gi+2$>k*?lX-58S4Oanj5H3)O6K`)zsTT--QmJckG(9>aIv)f)xs<*D{D4YWst)O zN{f_}n5cNz?+6`u{J(-`Dk|7$X>-Wy>IV2yqsG34QKON9PIP9{;5d6&p?+Z{GnwPY z{^|A`!md0G4WK7i6HF|rNY~Y4#-+Qh?ZxxwSfj7$S&mK$^*n=2ui~&EJ)>XAS* zD=`^ohPhZ(RRzqh+-j+!vQqNL%b)7p7Fy&$b-1A>9;^*#RKh4Lml)qa*()7<^~kTs zMuQg}H#sF`XkfrISFzA(m<|sQPfH8i#^zu(YV~iTCT`X?%(E5A_*^s$qBq;|=sFCM z9ZWxe?k#puQBmz29UMG+&t(v_Wi}hP1lRD zV&SXkm#3AlQgu_sC>SUymj+rxg;`9o_+%nG1bkN|86w*HdwY5muphJ89j-%PaIn$^ zpjcjBxwf*>+S(=yRkgO=)qKlUL}d3zf!0z-?64BM&DH5z`dl;IUH+3NA2Z|gN=hW| z`^riykN+{M4*ULH#n0EsI0w#F=<7SPnd(mfIwBe=H@z~QGtymUWpSDSacSEIJ-C<$;rw(-6c%V$oQb9cDX#8l%779r(PX(-hhd6X*@Oh zRY~+86SMBj^=fKTy}sVq*cgNer$ff!)d68%wVJZzd4Tj#_R@26q3hzam`*G?-#{b~ z1u223y1ceV!eI|O-4}R3l%6Ue3yl>Ufk3#qxq0v2Jz(6iY&IZU(%@z#B+P(92#qYM zhV;3doE!nOvHlyJ)4dO}vi>6#OqSC%{3w_*^744|OU-WuSr$GqGnXirm?|Gy3GwpQ z?SPJYWtUqDlOr$wN=9AS?i`DcLLw$PEzNE}ua?o=twij$@5vN_{c+lF1IGSK=P^>C)r`-e>qp2c)v@xG8ykqq zl|g@#A5kD3l~t+PU>Ae9c;{!YdxD#j zPIK=xU0rLcJUl=sE*7U&C+6VhcAFe49tiw&a)X=6I3Yc~%}Xg9mv zMrzGE%00q?B(L6JU^~*%=;++~Qs_EZ0)O^tM_j*VVYMdPWd-?_{ z91m6t_xC3zVlp#d09pXah`+(K#MU;c_DrJ82m_(-`(g50F@eJje*s^NLPzCmP~mMUC%=Q#2Dd>g3$oq?j{Fl1$KZ@*@Wb^S3m#fwZ+*X~Yo zqT;Z9^Zynrmfc=X?0?Ek6-^uK!Ce6lKt z^>aW<`{OT4pORh&iMJ1;DcnA2dxIvX& zH?b(XZ@2nGgwTxudlda6wHoIYPRsc}KG=BlicddHf+T-7>qT$Sd3rafb#6g;RtO`s^@O#;TW>^(jIf7CR#2acSPae;?E#@!7*eth1v` zg)S@qB@>g4=-WTNwU?)4PwPiU6ka10B_xCR7D&vgq_l8@XZ%wzz55{uwK1GlO~b>b ziHU#P!l)mNo8?VagW0C4qVmqsDNnKT&|cwDu5_y27*S9* z8!r(T!-uD6G?aU-6R-5AWsrjc7&$X14T7|7=VKHnr?bn8QDa-ckU&;k&v%;hCMSv3 zH(Z(mCB?*&$LLkc%snF_K7LlA8y;+IY#fcG-fy#abj(#M|K*eC*An8Hw(CvxE*X3$ zpiNMl^|eem_s7jj&BmK_3z3Xi3A+wW#=p9rFZwFD2;mZ9TCDZE4o-F;Vs zcqT3(SAtV&`;?-ot4l&&-tKsNwyv%YI6Z*4flRn%G%hZff1r5`vAk?)Z_hd{#e3V4 zi3=cSZLD~$v8-%ou31`CfoABhfUk&%=n~3p;FZ!zmMozmDlxGa7Z*4WA07(6L+*%o zd&)fY`}gnO-ktiBJtQO~0FjIn8GC{8sQ%? zF)CV(jK(UU$JU}@j~JK4*Vcae#0yF{$Ujv;h`LlOAKgVJoe$)`eB->_13sOxiOF=W zTP>7?sIjqpn>Cm%T(bg5?%g+6P!foMw*gI8Dt2Td)pqyt0!Ee*N|`1eFOf4@s3!^Z z##&4akj5J4`;JUW4Nc9EOL$B(svw_U)C zYQ&^(^F;AnTwGrKeUCFXK0eO=My1x)UkThzO;gj{pFeke$eB`pe9cvtz;>?Ws*`Wzv3)t!)&ZwxW*4o;+9jwZNeLhG#kgbu| zBx0!f>Hx|n;Ob%JksRexn1LTkz3$>au$#TfWDin(gDSgSRCIKyW6k+r9X&m$L{W7g z-@bd7nw$)VQd)AdcE^ubAby8X%>ZiTmLyfK+(UqLPxBckU)%5^Mh3USGaM{`m1SQVg1jKLHCUd>bPLpABa= zHe6QvQZl5|l$4b*i8&Nrro^rqfbSCvA9i(laqH&I+nn#Frl#O3_V)G)fq>iCz%zH# za-yf95fc+rsd07?6Z6~Fi|f=inhf@2ysxk5+qa(Pl0QpJkHA7*?20uqFmTwO;pG!jQW}F61(OE40yG#yBO^3Y zp!YzR5{Vy>keF#|?k@L|t#1C(X}=~(BYRRZGK@@2s>OzbpwN&I64H#UfFqhQ{?tw$ zGrIF~Awr#yi0JHKjm6^cAHA!4&!;PFjQ)7vCnhG&&CP}X13pi4Z78xhjz{ZJYy9(P zd1Zyud4IWguGzy4n?lUH*4pRvXLeFjDD6Nmtysqke8o}6IVPx@9K>5m2Y;HI@g6?( zK4DUnk$H%Nb1?Uv3cSkA@zOjvdq{{BNJ0s`Fp&6nnp1rooH@e^62?#1vWqWojW4p;us*o-@7LvEc`PZNOK-gA@~6h(vL46 zdE_~~Anb@?WdMNk^y%NyX;+xQc#1sM)86R&2e(0y2Flei%g4{( z03rao+Q@yfcl)b@q7Y^3?CflBr)Fjj$C3g|24=FCr)N6QwCB&S9~CL-&x(pD#}?=> zCdS5KS3}4|&&cTPct8*f(gOii=#Z!;EJfOC7&4Ey1+J67$0WqMj?^nSWO;8#C zG&RwwR>%M}6&5yt3k5t;j{*G|YTh)YVKOTRe*aE@2LYHAm@mxTYnArv*Z#gfdk*6G z?&}_2WYC|hUMpdvSnYBKGVx|oo;kEinDkbbmaDXQ5#^&VTYJ0bIMX$ zx*sAdf8yS}vRzjImCx70LONA!=ip$lzyA!%`kr1lpX+5L)>GX`L zX1s#XgaOE6ALQlpN|OXJNrr}oAcS##e*S%a9_C(jWaJkwFJIQtqPVy?u!Q5m>@+gU z%F0MdO$Do5smG45z|Pz|;2YS`Sm(~xwiX$=xiyXp-}3VEj)#*Kp|w8J;6C5xhu8qf zhq0_yx-bB7dfLY06@xG!<3IT`kinVr@$=h1Jz9Q55o%zl%WSr(LZGa?qw^Ut*f?S? zCI`skxJa^orv-TUDlW7HsKxjPTUV&yIL+-`$1h%LXlp;SM-6R#$m+WcJqB7L9UUF` zzMoMr;n7)vS`ZM>1r{xIhmOw9F9g0S&zm6P;J^`=kwIl6&&&IB$}2~$Ru|?~u4*L! ze__-Uu&%4CIS;gf9e_UqZZF`iAFs5A^3>IVHUNGwFlg0&9jzeNaO?itO$i0)v|bIb zu-RPrp~W%0e%<7UpOlR_4v2bWr(9 zOO8hUa(6+Lj4Y`^Fu>Tyd!*V(-ol~);%UD>-O6BwXpVnQ-|+Ai*;7rR4fP36pbuAG zUX*T)6eQQ-{9!hm61xtd4G;9aW`31b<~^h02@e@#DwgE~j9g{=U=-b&ZmhlKytTN&EE`hxteNJP`GL zeSK}Ltw9`Ohji4cZ*qRV`C+yqKyB{JW3A?UMD`qN0MD$g_hVSC=>9a?Ecfs_FYv5@ zrw!uV0>fX8UEA4KaKWLT!5OjzD`p$ z4`PhR_z*L5;A8wZSc_{*OKVH=W4(wh6 zRm{QB@lH56HX`QikP2J91Cs(CwHrWzcC`%guUUMBB@L}K7t4K=1IcuFyLfG#7%FUH9FZ#$2{6Sh)7LKB^IRZu8BY9zY z8JNsDq>}(7VDn8(P6BG#0b6k)kt-YnB-wUgUxI>ZK9$(ZW5rp}!F6?0XAU6u#mXdN z59!Q=gaj(-RK^tdl3RPOh-AO^g>M}7H^+h}CRCuImR}J+3=azvK)r88h|l=&(W4*V zzZYpXegjKJURD-R<6ch|-j6+v`<95jrb^xMx&DG@@@LMjo%b4c5 z4}xY)F13*5WMp8#c7}{2ECA>@A)%p>VyUn;0IWkKb=3F}uqhCB0lMU-&w#V!K-p7P zCLku35T{RS+uPl}gN{y3N0*zCG5Sl#_oIXqKOip14mlsJ;y_`C=xec?q=SG>z!X#z zU{NwHR#sM`5)!UQo3T*Df9OF}Y!3zq!nRF~jX5UU(SjYDw*h&lO2j_K#XSTilrG}B zW9jMXNv|~j{{0amume6{0JIBq{V@Fu;z6=|H>C<)M8hnkS;oecIzmmhYVcpqz!RwT z{Bo_|LXHsh5P+S~A2!VC^N2Yeg(@o!|Mc{{q@aMTV_$PKh}60uLBhg@by=U}1uX#$ z9UTCvQi-vmzP|o+jZ4M?%yd9>uCA`(;o$?X=EbMCfD==S{diLK7Ni+~1_L{*S#mkA zLGd^|EP-RI>|Y&N3C!1%i-tp!92p;XvbTpM7sOZi^6TJ9fM)3kFME3++)ik!gSl~l z$2wsEL4k%s4$KJ>!XB~%w!NiYGd)NDN3x%ie4WWdU-U-h*i}gtU&ts8r)cvrM>u5FL`md#>rN9Y5HvqhN zu)pu;@1LSN+}yh_i01wfN|AT@J`RG%>Hfoq5FuNH+Pt{Ptog?q7B=*<65m?0<+B?> zjL4sVrl%_?D^ok)!A9ucS)KUW^A3uby6cI+m~7E)P(PF1>pr$HmFYi3AcTYh66B2|_ zgR!0bA-u0I@Q>e^{r0WX?-TF_owqolkcg-6_Y9RA0yg1X>RxEC+q=$i;392kV;*48$W%G>bq4gcpv>3>mF{TC1arzRWZ z0m$MaIJnYw>soNh_D}gDz{BI77ay-JdaD;|UwlfMo~|w~9-e3=?y=YZEY}(g_i?~@ zZcG9Gs}>B1d1LYSFS+Qm{K`rL1Aua$XL5lj)O`AsmbPR!?Lrw=>rNd`FDWEc7Wd;h z+1^#`C|DMFbAL6V3IlGX@$>aXzjK$vdinjd>&1Vlq%|M)KLL`1TnyBwdn8;cV3>_9 z{N{#<02nOA7Z1h3u_O=S2^y#^JsVq8t9Bf#)z?m4QR6hgu5epZHCoLcNaz)IYbE4Q zHP?T<0{Qm=H4jL11w}>sP$)gfRp?kwhG>yLTU?AW&4y>#ZNdtf!o~Fr0+kiYqmjP8 zPy!!u;L_&e;pK%B+WMO40S)J5Pft&8@BQ91@!q~ZOKa z9c6I4^}1sKU*~|)WVs+>gg2h9R_(;j##T{Q=C^;SR%pAilm47Iki~7P3Nn*EgFIl1 zNuripot1oS{d{%=(H_`YkgHltH?a$~4{R(;%aF}I2~kl1{UO^DwJ-l&%zn%8>XieP zzdBbuNPs+jkNovVhjLZQuQ@zatEs8Ww_;+|#lX_mhVw|)R}4Tz?P(x;_G~^W%k^R* z0yNn*ljf!|$MF&q&9;|}j4E(#kOBbWB2GJ6sn>m*BpyIq}aAL`-s%{g^TbAbnyzc5!p_9*t4H;I0ZD9wtIT;xS zQ2UjiUp$*C`2l#cbrnaW&tj}l8lrR z8}}0)fC`{1VOcUzwKk59PEV^tdeKc=`}g??rKZ-koJvGQAH%Kw{(VADjyP($+m;$o zJdnt%EA31+)=^1EM@P$9Ss`1BVP~>6WB!!lD~OD3Z4dZNpHZ-}vX+-s?4Fz_6R>=} zNAM2A={_IfV>A+n@^WsmxCvl|vRU#_@L>H(>>c4raJzx%KFjYVm!qTu)D|==7}nu| zp|3g0dWO31Iz=Km3_`wrYlG#>W?-ON>FUy(C{S!%0umC)ckJm|X3*A-;y6LfmiYp; z^Lo`Qz%YP#k~x@#MtX~b-7Phqd{JfsvgiD=J#Zh`AOu#yi=_7&3vGj0xZ&Z0va-Xu>S=x5 zfC*4ZA!u$^rVd`hXOw#!4!g-PF98X6$8kn8IoW#MBRV}?v*UafJT?Q97F2Uc5YYVE zh0O*Kt|kfmmiQ|PXi1`nhq>Kwd7&QE3`hS*yr?H4l4fAL_~O#8X6J|#y*^xuce_4- z%QE?TR}U6s8SO?#0Xz}0S|&s12W=aT6p%mI*9O&i1PG;jWa@~Gk=79fB&09ApONqL zfue$JZx6WoPAZmawPj;(?+5Lg`-Q90F9!!+w6%YU==i}ZEoO!i+B(qJ2lg+S=d#U8 zVqTu40AVJd6TDDR>N*xc!2zTgy!ZWDlF-q~$l!)8Akf)C^Y+AIksl`T@fjEyg*)9K zAh7}93gzdab@Y9{uNWkB6OaPGV~5n*(k9iQF)#_~D^QX^NUJ)Qj{v37(h9`J=Uy6;L1ji`jC?zqm{?DIhl$2@Z8vv_o?RPW{I52|<_rZRYcjn@TFa)?x za47>1z+(qSGcX{-&d$y!1OjZI!0>^!~@!kuU-5HYaut_9}ya z=$n}2Bqs8qTG&{fT^vo5Ow>-xs&i9Qw|7t17PP_rfXC$-fl5ir*Ahg^p9mWhR8_J~ zjA6iRHzo{BP3gP?foZq5v|x2;oL=F<&IN-!Lg$eD{Cr^BkR#`cCwcS;AXgJ$RnT!^ z=|XI0u|)nl2cmQ=MS6YvKo|kG6Oldxa%>N}?A$pghiEqtc?@ z1t_FiWnTabOD;{KOG3hGu?vS0&I)oB=E=!YV2y~iu7SqR^3EI#4OnO_ren{3iO+Z- zk#b*UXJiO7nYQVIKL=DA+&i!=(MZ9$z~^>#1}hS*DOg-T#B{C3vobPBFPwL-9m_0$ zvz(mO=oS|BtQ5wR6<}xt1z}NyW`LG^ahP*G5y;7?mA53_czJIGHlOKpf|d%)6_Rmg z*|0AKqMhIK;-|qY$RLLVhq)LoQ<_)ZM@YlNzNk~!HPrd>CCvNSm{>Cka)?F1egVL+ zEF~5wPG_;~wjdm&R#iT%2U?m3qQX5w zjuCi#UonUK``dv@7x;hXQBj$w=z0ID`AQvl3f!~8^30#WwvLYP5fOO%4bW6A$|qpd zp_Hwzt(7LYz#s&<*4Y0zy46DmGr3W{g3)jw-BI-o?5zPu+UoM$fzLa-(g~(E^pFNG zU&WN99N-J#WY&f?zIgPmMRBlnauln_CMKL)N~f8JQZqBZVeJmetD}X3(yv%1FU-%M z1bcBZGBPkXr>}H>BMg65Mk|A85A_d3NpMvLMw-s{H?oqGAKILkZNic9)1!l(8;XwC zNtj0K^Y?XYu!pREx(&!vJq9QzC(f>L;cw$24cxs1AyN(}#w)jg03jc2Q1tn6Q~&An zmqCufw6rvDFO#G!t2f1Vpo4lgE92jN2cZ!~8eGz6FJ5FKZNP9|UibN3t1rwLE>Qma zKqX)$3^)QwI6OKk>Tso>^6-xj$TFuVC#rtF(XVx`rMkFzpViwa<7Kg?ruq8i5hrFk&%kmc|?UuAs~rRa$!G5 z9;h<8so}7#2BPVpzTGMP4^5G9d|~~sl&Y!=SUl?L>aXZg2N^(7>x3)1L!ZVc3CL{b z{hQ$P^9zuJmf3AqVXiYVGHp%5CX98(S%xD_{MXN3zm|F_(X9Piyt8AmGuBAUNE8*Y zo49u%EaI{6RMH(z2T(K}E>>k>GiBe*M?P=R538!2|B$$e#dB3xCDwO#ii?q}mt@q` z!1Z=7SP~En{wm533JbfKZ6-O|m>l2lWF~CFA#MgyX`sU{lVvAqsb}+?E{bP zKI`|r$seyk0fOxoAf7JHk_i$L5!!Bz;kS|-qF&F*tVUN_c6$hIx8r#CW0ttJuo!*xE`HB4rrv-$=`{@&dUY9da zZE&-oS9*@WdGiM5K2&pbsqcXDb>g`|H`ELmNqpbzzQDhtBzMeH1nq-Tq7l15&8l1r$=XN?RBr`uh6IFw(vX*{oKJ@5$`MDE794 z$zaW3!*xrf2RwnY*z;iU=2fCh-9-_rPt8-8{ZhQkrwaD=CXMAK*DE! zeckyKsI%*7#>#lMT_z{&5rx>ev*90r20-xOr3`yGQaF2VBvquQuH=M$f5@%(3Q_c4 zMT)|*{Pm?Wvn8p+s*88Q%RJ~r+yfKcPY zZk-+#rDXBpgQZB@WlPJfni}V0KQunARaJ$Jq$vA8?9g|f>w|^{8jK2^T~7iplxTy= z!|jE-7CiT4jrlCj>$s=7U|@~IMMZYD7$C_%f3(XYB0xphX8km?cb{Pw$%MC(;Dm1) z6Y;wl1DyK`wMPi&8{1h(K!lf(uF3Wo8*CL>_4MpPE>NHejvz5@zm75`chT>e^fMhZMmz zD( zK3pd>LPPg@k)fYKzp}{UG7Gu~Boh(z3)b>)B=olSHW1#2J-on+hP?>Qjg6{h=CdG% z5wMs(^8`ULhSTW~94;uvaHn0JohI4jp{?J;!en8OxBe}WHEy?{H~%;x1#@)uOjvuZk$>?sPq5)WwQ9Q-eTs^D z+}9m(1D*wf#}Hr*E;17A#eseCNGK@Sd|-9iZ1<1?Sf(%~Wnhq2Q>y_BbU+n0L(xTm zhXhTf8;mvF>K^R$fauh`zc7OT6=aY*Ouu zuNc#TMA5;)KT}h2iHWE7m-$htUcBh*>A{-NdHD!5YV86A&mdt~?oo^xRJ0%mM)iFJ zkuYbciud&8E-V14-*Y>#akfRbki}#QcK93V=nQ5^Qwj()g7CFyDqH%z0)_q~pfG9^ddjd)+-* zTkajTS~Q-nsaA~6{P*IuJK#-XGP*C$>vU}&7yv#7*%II97Pc4vu z1;p%Z2*oES&jTm}VbZ#qmZd zxIW0IV3ZC*u|iSX$t3>}SpoG=m?J+&pgtlX(9_lys=R}>0lATPO-*n;4*#s9YX+uq zfF5IF{D>2nzcgx+Q&QGC>@`8@8!KkS3^=(NRP;CGb#Qyw&J}us1^i2RxbzGRUnHCF z_N73X(>68+s+Bp`bx%>?6x7}8CA;#!=Nm-+MK$sN@5BGAL}^IUKf6KygbL^>Fsk4F zzeGwyU`s1>12Fa=b5Jt+pGQiQ$v}PyXb00c%$77!<2r3yRMXKR2J0W@jg%I|XkIg1 z!-G5j)UQElLatgC1BgJ|ee@q+W|NQs&{IK4p{Q8=20{pN9VDHGNDkOkTOi2}=cx@2 z4Z+`!)<^fi^@UJ(vg%`mGaA??AmiYWkVvB9GaD-i33-EeRpfgr19HE9Gf>_pm#Yid zpaBw-2QHmTwPP`;I9~PjK!8Lli{9&s2Y zN!UV3sD>RA03u<(%WThV11{e!$$q_#AVXD>U}$tQGS4<%AQFT}{wvz3a@K`;eMNZ? zjo61|!53d#MaI`lV4rM49|9+MZWuOa93FCki9|v1ZSwHZ+y?SNku#vHrNwvv%$>Ju<)2EtIwcMoIEi-lDxVQoL?hWkz7r9V^iT}4~NneGF z;EIQ5<=}7vU*;lh*VzFfTT@*EuJ6@8V|KQ;5(-5l=w3y3Xf%9f8sd?V*wmE@@hvQ@ zt*!O&s0U!?ezGU|r|I#dNB>A`zN%|ZgN>JfqEC^LAxMa-^q;|p!40ibB8ty0}+*yo-PE*67vZ7BA@k< zV6aY#)8*XvZ}q6@)Yxl)wBdR>8xE|X6jCm+ghYUzsa{_|{e_@EB^$-NZL%$C#u9bt zqd=0e`SuSEDq(N(hz+Q2k%f90(?Q7#1Kzjd61Ca+;AB| zzgD>v1ats17&|%v%?C9nB7)L#V~|=NbWCOPQ%0j&&migudUi-d)nk$VnVM2lSLY@r zjpb~7kE)nyd2T=R@)ksPIxbKu&yT6Frr7tYk!$jn)ES1E zI?ot5iGPo;IjLHW0-po}WX`rS+8JPYugfhIN9}y&wjAzUtG&qt$NgoEVkH8z6$s!9 zHr?uRgv=MT1zcZpIy#9D-+@b&sX(mrQSs8LU9YQq$qNdKMVRMMBRYw`s2!iKfvSEs z_?-%yLSh~ECIKtrPZDG~GNp@HTbvuGP0z_OF&(B|7!o|2+`m6zdW7+X>dl+7gSi{q z-tO-1ue6#Vm~;&kH*A5w>=!f>qu(*yE7jIrK-bSg#vq2|utme}`_GWJYyEXJ>4=Lvih%Gz;&TyLC=KEF$pnetm?xB_ zr)#wL)?Va5NCY(I&J+X|#mkr1Te+^|?Y8S9kVS;d??7D3ErpYtV=rPQjuv+p`s1B8 zS0GyD|9Eh#i=CgJPo%vK)aR2HvcUb?wd;7TzoKGraPVnns2JCGs2*eU%`2fmXBjE6WTqoF)-zt?(Z0icZ5xKyg;neDm&f*7(i z1!)JI-bH0I@mRLv#9x*`!k|5uyIt-*dVL{0lHTlDp`ZNuxaqvpGCL&I59q69ljN7 z4AO&UlNF0onLtHJoR4RtIy#PqasX7J!_J`5ZdbWlYM5j>9UV-Ou#`LRprM_#avoLf zsb39F^lp)KZa`Qtgj^Jw1rx+GmzS48pN6cB#$2kn(Q5{{c}2+hz=mncIA=?6|5av@ zev2~7-VBQ`dazrnZ^#Uc_O#xYDp zIvfC;6A%7~2twZg(%SOn5ZJ&xAAB0Z^5^w-rScxQCU=#xb}GbgYS0&XU26sni${%# z-K;*>ePnz*Cr%M7A$Ur=voPv8JAUs*?{Nu#%kNopLN@!Cpwu$cbtMctonM&Po;mnJ z0e-IY+dcZKr!8jcwR)WRvoXvkN(PGkS5tS;4kxD{{?1`@`0FW`B`zUxX!s3u)bdG| zX-SdA{qU1r_Y|({r!FSY(Toh=Afmq zfQ(#S(;6zTEnyZ(-%au0zr8noZlIEM6Dwe1+W@~ao+v9=7&eB@7z1w}%#Og=yttM; z1La8U(VAf}R^LDc``hR{oUJ-utz6nP4);dscG?fi94tmiO^Q?WLz24k;wo>4X2%B_ zm~yeB&BYu@GpBH(%z##j2aRwv$ zh2!c`oa_*OZB#7O^m`ZGdALzJ4PQz#>ZIi@c|NtHuoQA@y zG9Zp}FwUZ<2eh}kz`j5(>BB?vXV0Fov5jSgqDmAg<(SbZ$*fs38;{nyT`l(}K@6t` zvLB~-@D)5QPPZmr>`h;Ri)9Jnqr(B$H(Mx(1P@N9qr=L*F8KbXm(`bW%mIPw|JB-+ zhf}%s-CasWQjsL3QV~LBNMS2Obt)xO$=nGEn@DEfR6=MFl`$$)sE}leq9l}U$h1u% z!#1|>H zPkN|bQpBwR<#&j9wALgvI(19$GQj|rX<8uX#j;8CWuU9q+=>El+rR(vS8v%H*i6eU z5$*zgZatyC_GM2)N=K1E1QKFr(BVFwpASZN{XX6zN-%w-SB}vZyjoO8!@Xt5fYB$- zy>z77C0q7VFB9~lLP$iBU&rcGE2lT?ymmOOph6EZu_ z$H*03#$X=s9${u*9~vIsOXrmEK7()TAqcdaypvt)c7z9DzchpeS7RSxZIVS!zdTzU zgs4_6eH3#S#>U2oE`~lIY!|lmuESpd?G=+Uvq9-Jr*05l)D(A03O%)4hi$x}!e^DR z6>YcU;J0h>yiuIe$ooGcaBG_^quv(jS02O?Ydsz25I`aUl}^mS zQFCTtZ+!~y>%V}$aJVuS_L=GG_)nhUhU^g+N2y!HVl3Ome)5J2e`jJx zaf-JR0n_?fMVJR?0r$u5QJzV9NJ)ECOUu|3tK1E?PQ}E_|2!HNI@)q8u$*l>I$OS^ zy@QqR`?eX?nP*&FwH8tVxfywe;yiu>TkojG@J|Pg4O$Qz_Y2-SQdBF4z~mpemCEG z1Nh?GLT!D=w%*J>u3ZWsm(G3JjJNFMR}H;&lw8-3f!nYqjH9}#)!7Uh8nsXf_rfep zgcr$VvQcMw3-DawC}M!KUVcwcKHtBZS7eY&O<3zG9yQD&czic}d~`A{Jbq>tT%2az zykFhL1!p@59!jIgKnjXvFT{E82tYtL)7qDFCK97j`<&o0;`pif*3q%L{sRc`H8u=( z;t{g&fUA#pn}#2bKc+>?kf!3|a&7N96;cwk$4VDHil~H}e!6!Hp1Zm_zlBG8V9?z# zIa`yNsc}Eb$>)gBrZ+lv%u&q4m7TI4*!umeq%xHhtX()(~F%v^{lC$-+i(U*9Y!OUx*D4uZaAS`Gow2x)5Y{$ zk%bhjt9OO(j@;kbHpw9XFC-3pno+rEfSa&BifRCJG zQS$<^)wBacBmy70Fen8)Gj&XCg4#VYWZ-&H-F|d}Jo6<{P_Cs#-sB*=u|6uNdZ}h=Ku@?HvDMwHl-J0+I6(OB?aN9*O!!oa>G`@bA?~>w z0hmA0vP0$@QpWso4AKdYAIIIL@n-8cVr`%%36}BPs1*thPB+fGimC*5f{xWg8DBs) zMw;T@pSP%z`~48X8Gg+`e+C2));B{-nLSIa81_CTRyZmjrHB8)^O_u0q6e}HTIhj8 zT)tn-Hvfd!zS8%7e0)nxQiA;@zV_Is)aYvH@=|u*qB#*r_CUxhdL+53^KzEV(hXN% z3_fx1Trf0rb+n+_8dFD4va(uAg}iShdXxML9qt^8q26*k3^X_Tfsv%+tAz~+9{F#0 z{DV@rB##2KM~iT=Zyl!-wJMWjf+e%h;*G_12bNtgT-A3IlQwtT^&4VxC|K>jWX9NJ zUC-zc*I^*qM~`{Zvk&je%)Q{?5BmPQ_29cDiCQ6{p*@wHm?e856G@Rnlr03WCeCq( z@c)03{PWFSl37R^^8xYPj;7;*mI55CE4U`|y!hM(W5|-S>o5uzJm>0TVV?_Vwnyxg zGCRXSnRC(X5IMrJea)@T3j}1k*L+K`oxCC&Kx>a z#F{%zj}DL>thX(@dgaRQFx6X~<;HESq$Um;;QtoC%3@JIpQx|&QH0Z)+?Om~y!?K6 zSeV@R7_uwET8jJkb1JV072S?iqu`42ZN`PRk7Wk}>@yoKM6Yk7E3$$WtpPBiFd z-@U)>iI-lkTLaymkL)Ik+8T5_wEy9qJFBuMlXTuk@nA+Lh^}P1GzQ$j3hGf>uQEAL zDIqaI-Fd+qFBf#{NrhO#UX)agi4wF*&qTWMhOoEtFZMT>mByDQ?36`#a94yzlQ1wXB;;twjKf^>8p3p6dp&L@jjjenEDsTT>JM1afl-QQcyiYFV8d zn~+E(+21yQZ^&do;Z2<1 zSq^vNVqbl=kaF%k2o#eJDN!yc7}(13gE>8lyRchl);vt~L&#gw2|;1XM6jwj*}%+z z(BJ$1k@oB}MDA1x8?)yZvm4U)?e?teOn-+LO+|QVZ@dGQA{uvP2am?$E{(lL5T7e%!%;mIF(ohYDkHB zS6Fb0mGuxs?uYmXii0Lgb7@%_e(nyDbyWUC>3;z)falQ8x;*h^k}7=#>YoJWxs%Ch z2S}v2U)|pB?lsF35!5WAyHxKYUEhZf$GUW>26jlt?yyoQHwFR|H6c<<{N*8UR8>pK zgG1BPQ_yQDW^V3b))Wmse@UNKx^Azieik<`Z)jkk1atm&iWR~5!!29Rei}F497!UvekFb*;NFgw zXtXz@Bz>t*j)9>OXbt!0-O&OPkYCQ`G(A*Cj<|WV3ER24o0#X2ZN2a0>rZo^uV>)> zdUeh%WU>K$A7@BO6>6{p8LBZ<yON z)HwZKD+>BV33gW}Qyiu`O(QHzOKC4HL*h9c34*dAd7_I04QoL`RnMw89#PLpZu;#5 zSP5*-m6Y6U#gkv#4hJJ{4(UkNRLw+l)uQ94pw|rzBdML=GQ;N$_^%_+z<3ezc>#as znjffFFW97q9eO^I8ciu%3xa-Ns0HcX#*1AU+jRj^M_PMgTJNW6egqlk+MLj9}_LXss~9 z)meVn(oghAq1kX`x>u0sDfsw`$HU z%(pJsyMaeLOObh*SL^3IPnb5(ZETpcX{~*flseoOnsT-=G1pIdY5fy)pssJ`6>$ip z>R4iKp0@fYlayk*>X9Rxv+okRogk=9j#>$la5d|;;c`{^`+s^YvQYYdSMRRn9-i2G zO#jwjWbJSI{8tw1kNc@_;{;=&hsBzD{^aQLlVWV@u;5)2Nzd+E8~L&17zO@WTSl)X9pFZZ!FzR;=$UNW8>pX zKA;`FV}w8k0C8B9V1(P~skbyY*Gc@y#$xWbi_kM0D|Z;rtCBfi@@SXuQ%wX0(V=Y9D* zAb9A;`V%bVPHyh(^%eY57GK5B6fRA#%ibY;9`zEJKL6MHZsToSrIUb%QrxMq{U-Kk zFjNh=ZZFYL2ewAcoe8fUt8aTbz z_!UA+rK^{o7Ey`5^%KJpqx~)9C3tD1PIJPcSQj$oQ-modEZdYr8>LYoqzsTu(a>JKWyk=DC+guJ-3%nn87oooz`0s_PhF75oG&xHLUX`1}ld5HJw)z|mF ze(MRC&(FWMkLAXVp2_C-_&@QNr^>|`@zT*O04k5s$ef8-xvOP1L4o()BV6>My5QYr z3a$LO2VQnZnzE#;Dm#8dW9aB%(?j01cUXw}RjQ+3LRszfefP2A6KMj28%uAmw5=6O zU>Wp$eKA=~L0TiCc5r8!DQC%?foXPkV&rK^NC{hI$A}(l#l6y)%AjD^;P3~j8~464 z-9PvwfHQGx>}f`KjQg0?iPu*uiRWgvmPb3ZaN+eX>TjjxCu}EPed}TOY&{BZitJn{ z>!uslkTj~>We9oqIY>f2?tYhTLjA>yYypxwWKB~w4gygv-|)@6kl4K8_lY+h-K;n3 z=v8JC8WB}Peca($Hn!GfSGriWl-2gHblP(Eth=;4u>?pMsGl1rrXa=^Mtv66H55I6 z)27=mq!zsoi5JrskC96h{B5v(`^F{Nw^PW0Lr+Z!z_l#w&g6R95K-g6TG?>`U*=}Y z%veOFY5ms+#lh!A4Wt6ro^~DBZ=JPGW>Rt1zuCM@kOvC}A6ryGBZ5!Uz}0TY4*&f8 zi7xfr>1*>V@)N~tx6EDmh+^x*iK$rCw+Fr(8}oLsd}^hJY9~w3G;PNR8{K8LI#L(q z4Ls@3QxtO_^>#YrLr~9Dx#4iF*QpnM4Gw;|D<*#_NH^Otd8ljE(5^T=m1*7hE@B}^ zhGpIxabtB3L%T(N;1+A%h&PS^u3uUnf3G5jSIeaO*77^tz8BD1{m|2c)5vAK^)nuM zxU_7uDX~KootJUkTv@@duizt|Gi5`Zh-D#?-?w;{MviiZ8+~4*&m((4;dsT^pT z`foxhT{=VVUW8bxSg?>!