use-auto-width-input 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -34,6 +34,15 @@ var applyFontStyles = (styles, target) => {
34
34
  target.style.letterSpacing = styles.letterSpacing;
35
35
  target.style.fontWeight = styles.fontWeight;
36
36
  };
37
+ var applyStyles = (styles, target) => {
38
+ Object.keys(styles).forEach((styleAttr) => {
39
+ const key = styleAttr;
40
+ const value = styles[key];
41
+ if (typeof value === "string" && typeof key === "string") {
42
+ target.style.setProperty(key, value);
43
+ }
44
+ });
45
+ };
37
46
  var createGhostElement = (options) => {
38
47
  const tempElement = document.createElement("p");
39
48
  if (options?.minWidth) {
@@ -57,20 +66,27 @@ var createGhostElement = (options) => {
57
66
  return document.body.appendChild(tempElement);
58
67
  };
59
68
  var syncWidth = (inputRef, ghostElement) => {
60
- if (inputRef.current)
69
+ if (inputRef.current) {
61
70
  inputRef.current.style.width = `${ghostElement.current?.getBoundingClientRect()?.width ?? 0}px`;
71
+ return inputRef.current.style.width;
72
+ }
73
+ return null;
62
74
  };
63
75
 
64
76
  // src/use-auto-width-input.ts
65
77
  function useAutoWidthInput(inputRef, options) {
66
- const [width] = (0, import_react.useState)(options?.minWidth ?? 0);
78
+ const [width, setWidth] = (0, import_react.useState)(options?.minWidth ?? 0);
67
79
  const ghostElement = (0, import_react.useRef)(null);
68
80
  const input = inputRef.current;
81
+ const syncWidthToState = (inputRef2, ghostElement2) => {
82
+ const currentWidth = syncWidth(inputRef2, ghostElement2);
83
+ if (currentWidth) setWidth(currentWidth);
84
+ };
69
85
  const handleInput = (event) => {
70
86
  const target = event.target;
71
87
  if (ghostElement.current) {
72
88
  ghostElement.current.innerText = target.value;
73
- syncWidth(inputRef, ghostElement);
89
+ syncWidthToState(inputRef, ghostElement);
74
90
  }
75
91
  };
76
92
  const destroy = () => {
@@ -78,8 +94,28 @@ function useAutoWidthInput(inputRef, options) {
78
94
  ghostElement.current?.remove();
79
95
  ghostElement.current = null;
80
96
  };
97
+ const mountBehaviour = () => {
98
+ if (!inputRef.current) return;
99
+ if (ghostElement.current) return;
100
+ const styles = window.getComputedStyle(inputRef.current);
101
+ ghostElement.current = createGhostElement(options);
102
+ if (options?.ghostElement?.className)
103
+ ghostElement.current.classList.add(options?.ghostElement?.className);
104
+ ghostElement.current.id = options?.ghostElement?.id || "";
105
+ ghostElement.current.innerText = inputRef.current.value ?? "";
106
+ if (options?.minWidth) inputRef.current.style.minWidth = options?.minWidth;
107
+ if (options?.maxWidth) inputRef.current.style.maxWidth = options?.maxWidth;
108
+ applyFontStyles(styles, ghostElement.current);
109
+ if (options?.ghostElement?.styles)
110
+ applyStyles(options?.ghostElement?.styles, ghostElement.current);
111
+ syncWidthToState(inputRef, ghostElement);
112
+ inputRef.current.addEventListener("input", handleInput);
113
+ };
81
114
  (0, import_react.useEffect)(
82
- () => () => destroy(),
115
+ () => {
116
+ mountBehaviour();
117
+ return () => destroy();
118
+ },
83
119
  // eslint-disable-next-line react-hooks/exhaustive-deps
84
120
  []
85
121
  );
@@ -88,12 +124,7 @@ function useAutoWidthInput(inputRef, options) {
88
124
  inputRef.current = element;
89
125
  if (!element) destroy();
90
126
  else if (element && inputRef.current) {
91
- const styles = window.getComputedStyle(inputRef.current);
92
- ghostElement.current = createGhostElement(options);
93
- ghostElement.current.innerText = inputRef.current.value;
94
- applyFontStyles(styles, ghostElement.current);
95
- syncWidth(inputRef, ghostElement);
96
- inputRef.current.addEventListener("input", handleInput);
127
+ mountBehaviour();
97
128
  }
98
129
  },
99
130
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/use-auto-width-input.ts","../src/helpers.ts"],"sourcesContent":["export * from \"./use-auto-width-input\";\n\nexport * from \"./types\";\n","import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type RefObject,\n} from \"react\";\nimport type { AutWidthInputOptions } from \"./types\";\nimport { applyFontStyles, createGhostElement, syncWidth } from \"./helpers\";\n\nexport function useAutoWidthInput(\n inputRef: RefObject<HTMLInputElement | null>,\n options?: AutWidthInputOptions\n) {\n const [width] = useState(options?.minWidth ?? 0);\n const ghostElement = useRef<HTMLElement>(null);\n const input = inputRef.current;\n\n const handleInput = (event: Event) => {\n const target = event.target as HTMLInputElement;\n\n if (ghostElement.current) {\n ghostElement.current.innerText = target.value;\n\n syncWidth(inputRef, ghostElement);\n }\n };\n\n const destroy = () => {\n input?.removeEventListener(\"input\", handleInput);\n ghostElement.current?.remove();\n ghostElement.current = null;\n };\n\n useEffect(\n () => () => destroy(),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n []\n );\n\n const callbackRef = useCallback(\n (element: HTMLInputElement | null) => {\n inputRef.current = element;\n\n if (!element) destroy();\n else if (element && inputRef.current) {\n const styles = window.getComputedStyle(inputRef.current);\n ghostElement.current = createGhostElement(options);\n\n ghostElement.current.innerText = inputRef.current.value;\n applyFontStyles(styles, ghostElement.current);\n syncWidth(inputRef, ghostElement);\n\n inputRef.current.addEventListener(\"input\", handleInput);\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [inputRef]\n );\n\n return {\n width,\n callbackRef,\n ref: inputRef,\n };\n}\n","import type { RefObject } from \"react\";\nimport type { AutWidthInputOptions } from \"./types\";\n\nexport const applyFontStyles = (\n styles: CSSStyleDeclaration,\n target: HTMLElement\n) => {\n target.style.fontFamily = styles.fontFamily;\n target.style.fontSize = styles.fontSize;\n target.style.letterSpacing = styles.letterSpacing;\n target.style.fontWeight = styles.fontWeight;\n};\n\nexport const createGhostElement = (options?: AutWidthInputOptions) => {\n const tempElement = document.createElement(\"p\");\n\n if (options?.minWidth) {\n tempElement.style.minWidth = options?.minWidth;\n }\n\n Object.assign(tempElement.style, {\n position: \"absolute\",\n whiteSpace: \"pre\",\n top: \"0\",\n left: \"0\",\n visibility: \"hidden\",\n height: \"0\",\n overflow: \"hidden\",\n pointerEvents: \"none\",\n zIndex: -1000,\n margin: \"0\", // Reset margin to avoid layout interference\n });\n\n tempElement.innerText = \"\";\n tempElement.setAttribute(\"class\", \"ghost-paragraph-element\");\n\n return document.body.appendChild(tempElement);\n};\n\nexport const syncWidth = (\n inputRef: RefObject<HTMLInputElement | null>,\n ghostElement: RefObject<HTMLElement | null>\n) => {\n if (inputRef.current)\n inputRef.current.style.width = `${\n ghostElement.current?.getBoundingClientRect()?.width ?? 0\n }px`;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAMO;;;ACHA,IAAM,kBAAkB,CAC7B,QACA,WACG;AACH,SAAO,MAAM,aAAa,OAAO;AACjC,SAAO,MAAM,WAAW,OAAO;AAC/B,SAAO,MAAM,gBAAgB,OAAO;AACpC,SAAO,MAAM,aAAa,OAAO;AACnC;AAEO,IAAM,qBAAqB,CAAC,YAAmC;AACpE,QAAM,cAAc,SAAS,cAAc,GAAG;AAE9C,MAAI,SAAS,UAAU;AACrB,gBAAY,MAAM,WAAW,SAAS;AAAA,EACxC;AAEA,SAAO,OAAO,YAAY,OAAO;AAAA,IAC/B,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,QAAQ;AAAA;AAAA,EACV,CAAC;AAED,cAAY,YAAY;AACxB,cAAY,aAAa,SAAS,yBAAyB;AAE3D,SAAO,SAAS,KAAK,YAAY,WAAW;AAC9C;AAEO,IAAM,YAAY,CACvB,UACA,iBACG;AACH,MAAI,SAAS;AACX,aAAS,QAAQ,MAAM,QAAQ,GAC7B,aAAa,SAAS,sBAAsB,GAAG,SAAS,CAC1D;AACJ;;;ADrCO,SAAS,kBACd,UACA,SACA;AACA,QAAM,CAAC,KAAK,QAAI,uBAAS,SAAS,YAAY,CAAC;AAC/C,QAAM,mBAAe,qBAAoB,IAAI;AAC7C,QAAM,QAAQ,SAAS;AAEvB,QAAM,cAAc,CAAC,UAAiB;AACpC,UAAM,SAAS,MAAM;AAErB,QAAI,aAAa,SAAS;AACxB,mBAAa,QAAQ,YAAY,OAAO;AAExC,gBAAU,UAAU,YAAY;AAAA,IAClC;AAAA,EACF;AAEA,QAAM,UAAU,MAAM;AACpB,WAAO,oBAAoB,SAAS,WAAW;AAC/C,iBAAa,SAAS,OAAO;AAC7B,iBAAa,UAAU;AAAA,EACzB;AAEA;AAAA,IACE,MAAM,MAAM,QAAQ;AAAA;AAAA,IAEpB,CAAC;AAAA,EACH;AAEA,QAAM,kBAAc;AAAA,IAClB,CAAC,YAAqC;AACpC,eAAS,UAAU;AAEnB,UAAI,CAAC,QAAS,SAAQ;AAAA,eACb,WAAW,SAAS,SAAS;AACpC,cAAM,SAAS,OAAO,iBAAiB,SAAS,OAAO;AACvD,qBAAa,UAAU,mBAAmB,OAAO;AAEjD,qBAAa,QAAQ,YAAY,SAAS,QAAQ;AAClD,wBAAgB,QAAQ,aAAa,OAAO;AAC5C,kBAAU,UAAU,YAAY;AAEhC,iBAAS,QAAQ,iBAAiB,SAAS,WAAW;AAAA,MACxD;AAAA,IACF;AAAA;AAAA,IAEA,CAAC,QAAQ;AAAA,EACX;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,KAAK;AAAA,EACP;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/use-auto-width-input.ts","../src/helpers.ts"],"sourcesContent":["export * from \"./use-auto-width-input\";\n\nexport * from \"./types\";\n","import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type RefObject,\n} from \"react\";\nimport type { AutWidthInputOptions } from \"./types\";\nimport {\n applyFontStyles,\n applyStyles,\n createGhostElement,\n syncWidth,\n} from \"./helpers\";\n\ntype UseAutoWidthInputReturn = {\n width: string | number;\n callbackRef: (element: HTMLInputElement | null) => void;\n ref: RefObject<HTMLInputElement | null>;\n};\n\n// export function useAutoWidthInput(\n// inputRef: RefObject<HTMLInputElement | null>,\n// options?: AutWidthInputOptions\n// ): UseAutoWidthInputReturn;\n\n// export function useAutoWidthInput(\n// options?: AutWidthInputOptions\n// ): UseAutoWidthInputReturn;\n\nexport function useAutoWidthInput(\n inputRef: RefObject<HTMLInputElement | null>,\n options?: AutWidthInputOptions\n): UseAutoWidthInputReturn {\n const [width, setWidth] = useState(options?.minWidth ?? 0);\n const ghostElement = useRef<HTMLElement>(null);\n const input = inputRef.current;\n\n const syncWidthToState = (\n inputRef: RefObject<HTMLInputElement | null>,\n ghostElement: RefObject<HTMLElement | null>\n ) => {\n const currentWidth = syncWidth(inputRef, ghostElement);\n if (currentWidth) setWidth(currentWidth);\n };\n\n const handleInput = (event: Event) => {\n const target = event.target as HTMLInputElement;\n\n if (ghostElement.current) {\n ghostElement.current.innerText = target.value;\n\n syncWidthToState(inputRef, ghostElement);\n }\n };\n\n const destroy = () => {\n input?.removeEventListener(\"input\", handleInput);\n ghostElement.current?.remove();\n ghostElement.current = null;\n };\n\n const mountBehaviour = () => {\n if (!inputRef.current) return;\n\n // Doesnt make sense to mount an element if the element already exist\n if (ghostElement.current) return;\n\n const styles = window.getComputedStyle(inputRef.current);\n ghostElement.current = createGhostElement(options);\n\n if (options?.ghostElement?.className)\n ghostElement.current.classList.add(options?.ghostElement?.className);\n\n ghostElement.current.id = options?.ghostElement?.id || \"\";\n ghostElement.current.innerText = inputRef.current.value ?? \"\";\n\n if (options?.minWidth) inputRef.current.style.minWidth = options?.minWidth;\n if (options?.maxWidth) inputRef.current.style.maxWidth = options?.maxWidth;\n\n applyFontStyles(styles, ghostElement.current);\n\n // Here I allow you to mess up the styles of the hidden element\n if (options?.ghostElement?.styles)\n // NOTE: this will overwrite anything from the previus `applyFontStyles`\n applyStyles(options?.ghostElement?.styles, ghostElement.current);\n\n syncWidthToState(inputRef, ghostElement);\n\n inputRef.current.addEventListener(\"input\", handleInput);\n };\n\n useEffect(\n () => {\n mountBehaviour();\n return () => destroy();\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n []\n );\n\n const callbackRef = useCallback(\n (element: HTMLInputElement | null) => {\n inputRef.current = element;\n\n if (!element) destroy();\n else if (element && inputRef.current) {\n mountBehaviour();\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [inputRef]\n );\n\n return {\n width,\n callbackRef,\n ref: inputRef,\n };\n}\n","import type { RefObject } from \"react\";\nimport type { AutWidthInputOptions } from \"./types\";\n\nexport const applyFontStyles = (\n styles: CSSStyleDeclaration,\n target: HTMLElement\n) => {\n target.style.fontFamily = styles.fontFamily;\n target.style.fontSize = styles.fontSize;\n target.style.letterSpacing = styles.letterSpacing;\n target.style.fontWeight = styles.fontWeight;\n};\n\nexport const applyStyles = (\n styles: Partial<CSSStyleDeclaration>,\n target: HTMLElement\n) => {\n Object.keys(styles).forEach((styleAttr: string) => {\n const key = styleAttr as keyof CSSStyleDeclaration;\n\n const value = styles[key];\n\n if (typeof value === \"string\" && typeof key === \"string\") {\n target.style.setProperty(key, value);\n }\n });\n};\n\nexport const createGhostElement = (options?: AutWidthInputOptions) => {\n const tempElement = document.createElement(\"p\");\n\n if (options?.minWidth) {\n tempElement.style.minWidth = options?.minWidth;\n }\n\n Object.assign(tempElement.style, {\n position: \"absolute\",\n whiteSpace: \"pre\",\n top: \"0\",\n left: \"0\",\n visibility: \"hidden\",\n height: \"0\",\n overflow: \"hidden\",\n pointerEvents: \"none\",\n zIndex: -1000,\n margin: \"0\", // Reset margin to avoid layout interference\n });\n\n tempElement.innerText = \"\";\n tempElement.setAttribute(\"class\", \"ghost-paragraph-element\");\n\n return document.body.appendChild(tempElement);\n};\n\nexport const syncWidth = (\n inputRef: RefObject<HTMLInputElement | null>,\n ghostElement: RefObject<HTMLElement | null>\n) => {\n if (inputRef.current) {\n inputRef.current.style.width = `${\n ghostElement.current?.getBoundingClientRect()?.width ?? 0\n }px`;\n return inputRef.current.style.width;\n }\n\n return null;\n};\n"],"mappings":"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","names":["inputRef","ghostElement"]}
package/dist/index.d.cts CHANGED
@@ -2,12 +2,19 @@ import { RefObject } from 'react';
2
2
 
3
3
  type AutWidthInputOptions = {
4
4
  minWidth?: string;
5
+ maxWidth?: string;
6
+ ghostElement?: {
7
+ className?: string;
8
+ id?: string;
9
+ styles?: Partial<CSSStyleDeclaration>;
10
+ };
5
11
  };
6
12
 
7
- declare function useAutoWidthInput(inputRef: RefObject<HTMLInputElement | null>, options?: AutWidthInputOptions): {
13
+ type UseAutoWidthInputReturn = {
8
14
  width: string | number;
9
15
  callbackRef: (element: HTMLInputElement | null) => void;
10
16
  ref: RefObject<HTMLInputElement | null>;
11
17
  };
18
+ declare function useAutoWidthInput(inputRef: RefObject<HTMLInputElement | null>, options?: AutWidthInputOptions): UseAutoWidthInputReturn;
12
19
 
13
20
  export { type AutWidthInputOptions, useAutoWidthInput };
package/dist/index.d.ts CHANGED
@@ -2,12 +2,19 @@ import { RefObject } from 'react';
2
2
 
3
3
  type AutWidthInputOptions = {
4
4
  minWidth?: string;
5
+ maxWidth?: string;
6
+ ghostElement?: {
7
+ className?: string;
8
+ id?: string;
9
+ styles?: Partial<CSSStyleDeclaration>;
10
+ };
5
11
  };
6
12
 
7
- declare function useAutoWidthInput(inputRef: RefObject<HTMLInputElement | null>, options?: AutWidthInputOptions): {
13
+ type UseAutoWidthInputReturn = {
8
14
  width: string | number;
9
15
  callbackRef: (element: HTMLInputElement | null) => void;
10
16
  ref: RefObject<HTMLInputElement | null>;
11
17
  };
18
+ declare function useAutoWidthInput(inputRef: RefObject<HTMLInputElement | null>, options?: AutWidthInputOptions): UseAutoWidthInputReturn;
12
19
 
13
20
  export { type AutWidthInputOptions, useAutoWidthInput };
package/dist/index.js CHANGED
@@ -13,6 +13,15 @@ var applyFontStyles = (styles, target) => {
13
13
  target.style.letterSpacing = styles.letterSpacing;
14
14
  target.style.fontWeight = styles.fontWeight;
15
15
  };
16
+ var applyStyles = (styles, target) => {
17
+ Object.keys(styles).forEach((styleAttr) => {
18
+ const key = styleAttr;
19
+ const value = styles[key];
20
+ if (typeof value === "string" && typeof key === "string") {
21
+ target.style.setProperty(key, value);
22
+ }
23
+ });
24
+ };
16
25
  var createGhostElement = (options) => {
17
26
  const tempElement = document.createElement("p");
18
27
  if (options?.minWidth) {
@@ -36,20 +45,27 @@ var createGhostElement = (options) => {
36
45
  return document.body.appendChild(tempElement);
37
46
  };
38
47
  var syncWidth = (inputRef, ghostElement) => {
39
- if (inputRef.current)
48
+ if (inputRef.current) {
40
49
  inputRef.current.style.width = `${ghostElement.current?.getBoundingClientRect()?.width ?? 0}px`;
50
+ return inputRef.current.style.width;
51
+ }
52
+ return null;
41
53
  };
42
54
 
43
55
  // src/use-auto-width-input.ts
44
56
  function useAutoWidthInput(inputRef, options) {
45
- const [width] = useState(options?.minWidth ?? 0);
57
+ const [width, setWidth] = useState(options?.minWidth ?? 0);
46
58
  const ghostElement = useRef(null);
47
59
  const input = inputRef.current;
60
+ const syncWidthToState = (inputRef2, ghostElement2) => {
61
+ const currentWidth = syncWidth(inputRef2, ghostElement2);
62
+ if (currentWidth) setWidth(currentWidth);
63
+ };
48
64
  const handleInput = (event) => {
49
65
  const target = event.target;
50
66
  if (ghostElement.current) {
51
67
  ghostElement.current.innerText = target.value;
52
- syncWidth(inputRef, ghostElement);
68
+ syncWidthToState(inputRef, ghostElement);
53
69
  }
54
70
  };
55
71
  const destroy = () => {
@@ -57,8 +73,28 @@ function useAutoWidthInput(inputRef, options) {
57
73
  ghostElement.current?.remove();
58
74
  ghostElement.current = null;
59
75
  };
76
+ const mountBehaviour = () => {
77
+ if (!inputRef.current) return;
78
+ if (ghostElement.current) return;
79
+ const styles = window.getComputedStyle(inputRef.current);
80
+ ghostElement.current = createGhostElement(options);
81
+ if (options?.ghostElement?.className)
82
+ ghostElement.current.classList.add(options?.ghostElement?.className);
83
+ ghostElement.current.id = options?.ghostElement?.id || "";
84
+ ghostElement.current.innerText = inputRef.current.value ?? "";
85
+ if (options?.minWidth) inputRef.current.style.minWidth = options?.minWidth;
86
+ if (options?.maxWidth) inputRef.current.style.maxWidth = options?.maxWidth;
87
+ applyFontStyles(styles, ghostElement.current);
88
+ if (options?.ghostElement?.styles)
89
+ applyStyles(options?.ghostElement?.styles, ghostElement.current);
90
+ syncWidthToState(inputRef, ghostElement);
91
+ inputRef.current.addEventListener("input", handleInput);
92
+ };
60
93
  useEffect(
61
- () => () => destroy(),
94
+ () => {
95
+ mountBehaviour();
96
+ return () => destroy();
97
+ },
62
98
  // eslint-disable-next-line react-hooks/exhaustive-deps
63
99
  []
64
100
  );
@@ -67,12 +103,7 @@ function useAutoWidthInput(inputRef, options) {
67
103
  inputRef.current = element;
68
104
  if (!element) destroy();
69
105
  else if (element && inputRef.current) {
70
- const styles = window.getComputedStyle(inputRef.current);
71
- ghostElement.current = createGhostElement(options);
72
- ghostElement.current.innerText = inputRef.current.value;
73
- applyFontStyles(styles, ghostElement.current);
74
- syncWidth(inputRef, ghostElement);
75
- inputRef.current.addEventListener("input", handleInput);
106
+ mountBehaviour();
76
107
  }
77
108
  },
78
109
  // eslint-disable-next-line react-hooks/exhaustive-deps
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/use-auto-width-input.ts","../src/helpers.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type RefObject,\n} from \"react\";\nimport type { AutWidthInputOptions } from \"./types\";\nimport { applyFontStyles, createGhostElement, syncWidth } from \"./helpers\";\n\nexport function useAutoWidthInput(\n inputRef: RefObject<HTMLInputElement | null>,\n options?: AutWidthInputOptions\n) {\n const [width] = useState(options?.minWidth ?? 0);\n const ghostElement = useRef<HTMLElement>(null);\n const input = inputRef.current;\n\n const handleInput = (event: Event) => {\n const target = event.target as HTMLInputElement;\n\n if (ghostElement.current) {\n ghostElement.current.innerText = target.value;\n\n syncWidth(inputRef, ghostElement);\n }\n };\n\n const destroy = () => {\n input?.removeEventListener(\"input\", handleInput);\n ghostElement.current?.remove();\n ghostElement.current = null;\n };\n\n useEffect(\n () => () => destroy(),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n []\n );\n\n const callbackRef = useCallback(\n (element: HTMLInputElement | null) => {\n inputRef.current = element;\n\n if (!element) destroy();\n else if (element && inputRef.current) {\n const styles = window.getComputedStyle(inputRef.current);\n ghostElement.current = createGhostElement(options);\n\n ghostElement.current.innerText = inputRef.current.value;\n applyFontStyles(styles, ghostElement.current);\n syncWidth(inputRef, ghostElement);\n\n inputRef.current.addEventListener(\"input\", handleInput);\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [inputRef]\n );\n\n return {\n width,\n callbackRef,\n ref: inputRef,\n };\n}\n","import type { RefObject } from \"react\";\nimport type { AutWidthInputOptions } from \"./types\";\n\nexport const applyFontStyles = (\n styles: CSSStyleDeclaration,\n target: HTMLElement\n) => {\n target.style.fontFamily = styles.fontFamily;\n target.style.fontSize = styles.fontSize;\n target.style.letterSpacing = styles.letterSpacing;\n target.style.fontWeight = styles.fontWeight;\n};\n\nexport const createGhostElement = (options?: AutWidthInputOptions) => {\n const tempElement = document.createElement(\"p\");\n\n if (options?.minWidth) {\n tempElement.style.minWidth = options?.minWidth;\n }\n\n Object.assign(tempElement.style, {\n position: \"absolute\",\n whiteSpace: \"pre\",\n top: \"0\",\n left: \"0\",\n visibility: \"hidden\",\n height: \"0\",\n overflow: \"hidden\",\n pointerEvents: \"none\",\n zIndex: -1000,\n margin: \"0\", // Reset margin to avoid layout interference\n });\n\n tempElement.innerText = \"\";\n tempElement.setAttribute(\"class\", \"ghost-paragraph-element\");\n\n return document.body.appendChild(tempElement);\n};\n\nexport const syncWidth = (\n inputRef: RefObject<HTMLInputElement | null>,\n ghostElement: RefObject<HTMLElement | null>\n) => {\n if (inputRef.current)\n inputRef.current.style.width = `${\n ghostElement.current?.getBoundingClientRect()?.width ?? 0\n }px`;\n};\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;;;ACHA,IAAM,kBAAkB,CAC7B,QACA,WACG;AACH,SAAO,MAAM,aAAa,OAAO;AACjC,SAAO,MAAM,WAAW,OAAO;AAC/B,SAAO,MAAM,gBAAgB,OAAO;AACpC,SAAO,MAAM,aAAa,OAAO;AACnC;AAEO,IAAM,qBAAqB,CAAC,YAAmC;AACpE,QAAM,cAAc,SAAS,cAAc,GAAG;AAE9C,MAAI,SAAS,UAAU;AACrB,gBAAY,MAAM,WAAW,SAAS;AAAA,EACxC;AAEA,SAAO,OAAO,YAAY,OAAO;AAAA,IAC/B,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,QAAQ;AAAA;AAAA,EACV,CAAC;AAED,cAAY,YAAY;AACxB,cAAY,aAAa,SAAS,yBAAyB;AAE3D,SAAO,SAAS,KAAK,YAAY,WAAW;AAC9C;AAEO,IAAM,YAAY,CACvB,UACA,iBACG;AACH,MAAI,SAAS;AACX,aAAS,QAAQ,MAAM,QAAQ,GAC7B,aAAa,SAAS,sBAAsB,GAAG,SAAS,CAC1D;AACJ;;;ADrCO,SAAS,kBACd,UACA,SACA;AACA,QAAM,CAAC,KAAK,IAAI,SAAS,SAAS,YAAY,CAAC;AAC/C,QAAM,eAAe,OAAoB,IAAI;AAC7C,QAAM,QAAQ,SAAS;AAEvB,QAAM,cAAc,CAAC,UAAiB;AACpC,UAAM,SAAS,MAAM;AAErB,QAAI,aAAa,SAAS;AACxB,mBAAa,QAAQ,YAAY,OAAO;AAExC,gBAAU,UAAU,YAAY;AAAA,IAClC;AAAA,EACF;AAEA,QAAM,UAAU,MAAM;AACpB,WAAO,oBAAoB,SAAS,WAAW;AAC/C,iBAAa,SAAS,OAAO;AAC7B,iBAAa,UAAU;AAAA,EACzB;AAEA;AAAA,IACE,MAAM,MAAM,QAAQ;AAAA;AAAA,IAEpB,CAAC;AAAA,EACH;AAEA,QAAM,cAAc;AAAA,IAClB,CAAC,YAAqC;AACpC,eAAS,UAAU;AAEnB,UAAI,CAAC,QAAS,SAAQ;AAAA,eACb,WAAW,SAAS,SAAS;AACpC,cAAM,SAAS,OAAO,iBAAiB,SAAS,OAAO;AACvD,qBAAa,UAAU,mBAAmB,OAAO;AAEjD,qBAAa,QAAQ,YAAY,SAAS,QAAQ;AAClD,wBAAgB,QAAQ,aAAa,OAAO;AAC5C,kBAAU,UAAU,YAAY;AAEhC,iBAAS,QAAQ,iBAAiB,SAAS,WAAW;AAAA,MACxD;AAAA,IACF;AAAA;AAAA,IAEA,CAAC,QAAQ;AAAA,EACX;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,KAAK;AAAA,EACP;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/use-auto-width-input.ts","../src/helpers.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type RefObject,\n} from \"react\";\nimport type { AutWidthInputOptions } from \"./types\";\nimport {\n applyFontStyles,\n applyStyles,\n createGhostElement,\n syncWidth,\n} from \"./helpers\";\n\ntype UseAutoWidthInputReturn = {\n width: string | number;\n callbackRef: (element: HTMLInputElement | null) => void;\n ref: RefObject<HTMLInputElement | null>;\n};\n\n// export function useAutoWidthInput(\n// inputRef: RefObject<HTMLInputElement | null>,\n// options?: AutWidthInputOptions\n// ): UseAutoWidthInputReturn;\n\n// export function useAutoWidthInput(\n// options?: AutWidthInputOptions\n// ): UseAutoWidthInputReturn;\n\nexport function useAutoWidthInput(\n inputRef: RefObject<HTMLInputElement | null>,\n options?: AutWidthInputOptions\n): UseAutoWidthInputReturn {\n const [width, setWidth] = useState(options?.minWidth ?? 0);\n const ghostElement = useRef<HTMLElement>(null);\n const input = inputRef.current;\n\n const syncWidthToState = (\n inputRef: RefObject<HTMLInputElement | null>,\n ghostElement: RefObject<HTMLElement | null>\n ) => {\n const currentWidth = syncWidth(inputRef, ghostElement);\n if (currentWidth) setWidth(currentWidth);\n };\n\n const handleInput = (event: Event) => {\n const target = event.target as HTMLInputElement;\n\n if (ghostElement.current) {\n ghostElement.current.innerText = target.value;\n\n syncWidthToState(inputRef, ghostElement);\n }\n };\n\n const destroy = () => {\n input?.removeEventListener(\"input\", handleInput);\n ghostElement.current?.remove();\n ghostElement.current = null;\n };\n\n const mountBehaviour = () => {\n if (!inputRef.current) return;\n\n // Doesnt make sense to mount an element if the element already exist\n if (ghostElement.current) return;\n\n const styles = window.getComputedStyle(inputRef.current);\n ghostElement.current = createGhostElement(options);\n\n if (options?.ghostElement?.className)\n ghostElement.current.classList.add(options?.ghostElement?.className);\n\n ghostElement.current.id = options?.ghostElement?.id || \"\";\n ghostElement.current.innerText = inputRef.current.value ?? \"\";\n\n if (options?.minWidth) inputRef.current.style.minWidth = options?.minWidth;\n if (options?.maxWidth) inputRef.current.style.maxWidth = options?.maxWidth;\n\n applyFontStyles(styles, ghostElement.current);\n\n // Here I allow you to mess up the styles of the hidden element\n if (options?.ghostElement?.styles)\n // NOTE: this will overwrite anything from the previus `applyFontStyles`\n applyStyles(options?.ghostElement?.styles, ghostElement.current);\n\n syncWidthToState(inputRef, ghostElement);\n\n inputRef.current.addEventListener(\"input\", handleInput);\n };\n\n useEffect(\n () => {\n mountBehaviour();\n return () => destroy();\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n []\n );\n\n const callbackRef = useCallback(\n (element: HTMLInputElement | null) => {\n inputRef.current = element;\n\n if (!element) destroy();\n else if (element && inputRef.current) {\n mountBehaviour();\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [inputRef]\n );\n\n return {\n width,\n callbackRef,\n ref: inputRef,\n };\n}\n","import type { RefObject } from \"react\";\nimport type { AutWidthInputOptions } from \"./types\";\n\nexport const applyFontStyles = (\n styles: CSSStyleDeclaration,\n target: HTMLElement\n) => {\n target.style.fontFamily = styles.fontFamily;\n target.style.fontSize = styles.fontSize;\n target.style.letterSpacing = styles.letterSpacing;\n target.style.fontWeight = styles.fontWeight;\n};\n\nexport const applyStyles = (\n styles: Partial<CSSStyleDeclaration>,\n target: HTMLElement\n) => {\n Object.keys(styles).forEach((styleAttr: string) => {\n const key = styleAttr as keyof CSSStyleDeclaration;\n\n const value = styles[key];\n\n if (typeof value === \"string\" && typeof key === \"string\") {\n target.style.setProperty(key, value);\n }\n });\n};\n\nexport const createGhostElement = (options?: AutWidthInputOptions) => {\n const tempElement = document.createElement(\"p\");\n\n if (options?.minWidth) {\n tempElement.style.minWidth = options?.minWidth;\n }\n\n Object.assign(tempElement.style, {\n position: \"absolute\",\n whiteSpace: \"pre\",\n top: \"0\",\n left: \"0\",\n visibility: \"hidden\",\n height: \"0\",\n overflow: \"hidden\",\n pointerEvents: \"none\",\n zIndex: -1000,\n margin: \"0\", // Reset margin to avoid layout interference\n });\n\n tempElement.innerText = \"\";\n tempElement.setAttribute(\"class\", \"ghost-paragraph-element\");\n\n return document.body.appendChild(tempElement);\n};\n\nexport const syncWidth = (\n inputRef: RefObject<HTMLInputElement | null>,\n ghostElement: RefObject<HTMLElement | null>\n) => {\n if (inputRef.current) {\n inputRef.current.style.width = `${\n ghostElement.current?.getBoundingClientRect()?.width ?? 0\n }px`;\n return inputRef.current.style.width;\n }\n\n return null;\n};\n"],"mappings":"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","names":["inputRef","ghostElement"]}
package/package.json CHANGED
@@ -1,11 +1,16 @@
1
1
  {
2
2
  "name": "use-auto-width-input",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "A React hook to automatically resize an input based on its content",
5
5
  "author": "Luis Vanin",
6
6
  "module": "./dist/index.js",
7
7
  "main": "./dist/index.cjs",
8
8
  "types": "/dist/index.d.ts",
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/LuigiVanin/use-auto-width-input"
13
+ },
9
14
  "files": [
10
15
  "dist"
11
16
  ],