@ihabdevteam/core 0.9.3 → 0.9.4

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.
@@ -165,17 +165,22 @@ export { default as ConfirmPopup } from './components/Popup/ConfirmPopup';
165
165
  export { default as CouponInputPopup } from './components/Popup/CouponInputPopup';
166
166
  export { default as MenuTooltip } from './components/Tooltip/MenuTooltip';
167
167
  export { default as InfoTooltip } from './components/Tooltip/InfoTooltip';
168
- /* 배럴 이름이 파일명과 뒤집혀 있었다(TooltipInfo). 소비자가 그것을 알아채고
169
- `import { TooltipInfo as InfoTooltip }` 도로 바꿔 쓰고 있었다 — 이름이 틀렸다는
170
- 가장 분명한 증거다. 파일명·형제 컴포넌트(DescriptionTooltip, MenuTooltip)와 맞췄다.
171
- 옛 이름은 깨뜨리지 않으려고 남겨둔다. 소비자가 다 옮기면 지운다. */
168
+ /* 배럴 이름이 파일명과 뒤집혀 있었다(TooltipInfo). 파일명·형제 컴포넌트
169
+ (DescriptionTooltip, MenuTooltip)와 맞췄다 배럴이 자기 안에서도 갈려 있었다.
170
+ 이름은 깨뜨리지 않으려고 남겨둔다. 소비자가 다 옮기면 지운다.
171
+
172
+ 처음에는 "소비자가 `as InfoTooltip` 로 도로 바꿔 쓰고 있다" 를 근거로 들었는데,
173
+ 그 줄은 이 저장소 playground 의 내 코드였다(speechear-v2 에는 0건). 남의 증거로
174
+ 착각한 것이라 바로잡는다. 근거는 위의 두 가지 — 파일명 불일치와 배럴 안의 불일치
175
+ — 로 충분하고, 내가 그 자리에서 이름을 도로 바꿔 적었다는 것 자체도 같은 마찰을
176
+ 겪었다는 뜻이긴 하다. 다만 그건 남이 아니라 나다. */
172
177
  export { default as TooltipInfo } from './components/Tooltip/InfoTooltip';
173
178
  /* 제목 + 사유 목록을 띄우는 툴팁. 컴포넌트는 진작 있었는데 배럴에 없어 소비자가
174
179
  쓸 수 없었다(어드민이 '왜 재생성 권고인지'를 이걸로 알린다). */
175
180
  export { default as DescriptionTooltip } from './components/Tooltip/DescriptionTooltip';
176
181
  export { default as TextBoxTooltip } from './components/Tooltip/TextBoxTooltip';
177
- /* 같은 까닭. 이쪽은 옛 이름을 쓰는 소비자가곳도 없어(실측 0건) 원래는 그냥
178
- 바꿔도 됐지만, 짝이 되는 InfoTooltip 과 다르게 다룰 까닭이 없어 나란히 남긴다. */
182
+ /* 같은 까닭. 이쪽은 옛 이름을 쓰는 곳이군데도 없어(실측 0건) 그냥 바꿔도
183
+ 됐지만, 짝이 되는 InfoTooltip 과 다르게 다룰 까닭이 없어 나란히 남긴다. */
179
184
  export { default as TooltipTextBox } from './components/Tooltip/TextBoxTooltip';
180
185
  export { default as TrainingTooltip, TRAINING_TOOLTIP_HOVER_DELAY_MS } from './components/Tooltip/TrainingTooltip';
181
186
  export { default as EffectPartyPopper } from './components/Effect/EffectPartyPopper';
@@ -138,16 +138,48 @@ export function useWordTrainingFilter(groupKey, caps, opts) {
138
138
  setContrastCount(minDiffCount);
139
139
  }, [minDiffCount, contrastCount]);
140
140
  /* eslint-enable react-hooks/set-state-in-effect */
141
+ // groupKey 는 마운트 때 한 번만 읽힌다(saved 가 lazy init). 그런데 저장 effect 는
142
+ // groupKey 를 의존성에 두고 **지금 든 값**을 새 열쇠에 쓴다. 그래서 살아 있는 훅에서
143
+ // groupKey 를 바꾸면 앞 그룹의 값이 뒤 그룹 저장소를 덮어썼다 — 실측으로 B 에 있던
144
+ // (4, ['초3']) 이 A 의 (3, []) 로 지워졌다. 사용자가 저장해 둔 설정이 사라진다.
145
+ //
146
+ // 지금 소비자는 상수('common')를 넘겨 여기에 안 닿는다. 그래도 첫 인자 이름이
147
+ // '그룹'이라 두 그룹을 다루려면 자연히 바꾸게 되는 자리다 — API 가 스스로를
148
+ // 망가뜨리는 부름을 권하고 있었다.
149
+ //
150
+ // 열쇠가 바뀌면 그 열쇠의 저장값을 다시 읽어 얹고, 얹기 전에는 쓰지 않는다.
151
+ const [hydratedKey, setHydratedKey] = useState(groupKey);
152
+ useEffect(() => {
153
+ if (hydratedKey === groupKey)
154
+ return;
155
+ if (!opts?.resetOnMount) {
156
+ const s = loadSavedFilter(groupKey);
157
+ const n = typeof s?.contrastCount === 'number' && Number.isFinite(s.contrastCount) ? s.contrastCount : 1;
158
+ setContrastCount(Math.min(5, Math.max(1, n)));
159
+ setComposition(s?.composition ?? seedComposition());
160
+ setExcludeNoImage(s?.excludeNoImage ?? true);
161
+ setSameStructureOnly(s?.sameStructureOnly ?? true);
162
+ setPhonemeView(s?.phonemeView === 'pron' ? 'pron' : 'char');
163
+ setGradeFilter(Array.isArray(s?.gradeFilter) ? s.gradeFilter : []);
164
+ setPosFilter(Array.isArray(s?.posFilter) ? s.posFilter : []);
165
+ setWordTypeFilter(Array.isArray(s?.wordTypeFilter) ? s.wordTypeFilter : []);
166
+ }
167
+ setHydratedKey(groupKey);
168
+ // seedComposition 은 caps 파생이라 groupKey 와 함께 갱신된다
169
+ // eslint-disable-next-line react-hooks/exhaustive-deps
170
+ }, [groupKey, hydratedKey, opts?.resetOnMount]);
141
171
  // 필터 변경 시 그룹별 localStorage에 저장. difficulty는 composition에서 파생되므로 저장 대상에서 제외.
142
172
  // resetOnMount 그룹은 매 진입 기본값이어야 하므로 저장하지 않는다(다음 진입에 새어들지 않게).
143
173
  useEffect(() => {
144
174
  if (opts?.resetOnMount)
145
175
  return;
176
+ if (hydratedKey !== groupKey)
177
+ return; // 새 열쇠를 아직 안 얹었다 — 지금 쓰면 앞 그룹 값을 덮는다
146
178
  try {
147
179
  localStorage.setItem(storageKey(groupKey), JSON.stringify({ contrastCount, composition, excludeNoImage, sameStructureOnly, phonemeView, gradeFilter, posFilter, wordTypeFilter }));
148
180
  }
149
181
  catch { }
150
- }, [opts?.resetOnMount, groupKey, contrastCount, composition, excludeNoImage, sameStructureOnly, phonemeView, gradeFilter, posFilter, wordTypeFilter]);
182
+ }, [opts?.resetOnMount, groupKey, hydratedKey, contrastCount, composition, excludeNoImage, sameStructureOnly, phonemeView, gradeFilter, posFilter, wordTypeFilter]);
151
183
  const [poolVersion, setPoolVersion] = useState(0);
152
184
  // 단어 풀 갱신 이벤트 구독
153
185
  useWindowEvent('wordpool:update', () => setPoolVersion(v => v + 1));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ihabdevteam/core",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "private": false,
5
5
  "main": "dist/ihabdevteam-core.cjs.js",
6
6
  "module": "dist/ihabdevteam-core.esm.js",